---
title: Charmhub | Deploy Loki using Charmhub - The Open Operator Collection
description: Deploy the latest version of Loki as a Kubernetes Operator on any cloud.
url: https://charmhub.io/loki-k8s/libraries/loki_push_api
---

# Loki

[Canonical Observability](https://charmhub.io/publisher/observability "View all packages from Canonical Observability")

* [Canonical Observability](https://charmhub.io/publisher/observability "View all packages from Canonical Observability")

Platform:

3.7/stable tf-3.7.1-1-g5178fa41f

```
juju deploy loki-k8s --channel 3.7/stable
```

[Learn to deploy on juju >](https://juju.is/docs/juju/manage-applications)

[Toggle side navigation](https://charmhub.io/loki-k8s/libraries/loki_push_api#drawer)

## charms.loki\_k8s.v1.loki\_push\_api

* [*Docstrings*Docstrings](https://charmhub.io/loki-k8s/libraries/loki_push_api)
  [*Code*Source code](https://charmhub.io/loki-k8s/libraries/loki_push_api/source-code)
* + Download

    Fetch library

    ```
    charmcraft fetch-lib charms.loki_k8s.v1.loki_push_api
    ```

    [Download loki\_push\_api.py](https://charmhub.io/loki-k8s/libraries/loki_push_api/download)
  + *Last updated* 13 Oct 2026
  + *Revision* Library version 1.35

##### Overview.

This document explains how to use the two principal objects this library provides:

* `LokiPushApiProvider`: This object is meant to be used by any Charmed Operator that needs to
  implement the provider side of the `loki_push_api` relation interface. For instance, a Loki charm.
  The provider side of the relation represents the server side, to which logs are being pushed.
* `LokiPushApiConsumer`: This object is meant to be used by any Charmed Operator that needs to
  send log to Loki by implementing the consumer side of the `loki_push_api` relation interface.
  For instance, a Promtail or Grafana agent charm which needs to send logs to Loki.
* `LogProxyConsumer`: DEPRECATED.
  This object can be used by any Charmed Operator which needs to send telemetry, such as logs, to
  Loki through a Log Proxy by implementing the consumer side of the `loki_push_api` relation
  interface.
  In order to be able to control the labels on the logs pushed this object adds a Pebble layer
  that runs Promtail in the workload container, injecting Juju topology labels into the
  logs on the fly.
  This object is deprecated. Consider migrating to LogForwarder with the release of Juju 3.6 LTS.
* `LogForwarder`: This object can be used by any Charmed Operator which needs to send the workload
  standard output (stdout) through Pebble's log forwarding mechanism, to Loki endpoints through the
  `loki_push_api` relation interface.
  In order to be able to control the labels on the logs pushed this object updates the pebble layer's
  "log-targets" section with Juju topology.

Filtering logs in Loki is largely performed on the basis of labels. In the Juju ecosystem, Juju
topology labels are used to uniquely identify the workload which generates telemetry like logs.

##### LokiPushApiProvider Library Usage

This object may be used by any Charmed Operator which implements the `loki_push_api` interface.
For instance, Loki or Grafana Agent.

For this purpose a charm needs to instantiate the `LokiPushApiProvider` object with one mandatory
and three optional arguments.

* `charm`: A reference to the parent (Loki) charm.
* `relation_name`: The name of the relation that the charm uses to interact
  with its clients, which implement `LokiPushApiConsumer` `LogForwarder`, or `LogProxyConsumer`
  (note that LogProxyConsumer is deprecated).

  If provided, this relation name must match a provided relation in metadata.yaml with the
  `loki_push_api` interface.

  The default relation name is "logging" for `LokiPushApiConsumer` and `LogForwarder`, and
  "log-proxy" for `LogProxyConsumer` (note that LogProxyConsumer is deprecated).

  For example, a provider's `metadata.yaml` file may look as follows:

  ```
  provides:
    logging:
      interface: loki_push_api
  ```

  Subsequently, a Loki charm may instantiate the `LokiPushApiProvider` in its constructor as
  follows:

  ```
  from charms.loki_k8s.v1.loki_push_api import LokiPushApiProvider
  from loki_server import LokiServer
  ...

  class LokiOperatorCharm(CharmBase):
      ...

      def __init__(self, *args):
          super().__init__(*args)
          ...
          external_url = urlparse(self._external_url)
          self.loki_provider = LokiPushApiProvider(
              self,
              port=external_url.port or 80,
              scheme=external_url.scheme,
              path=f"{external_url.path}/loki/api/v1/push",
          )
          ...
  ```

  + `port`: Loki Push Api endpoint port. Default value: `3100`.
  + `scheme`: Loki Push Api endpoint scheme (`HTTP` or `HTTPS`). Default value: `HTTP`
  + `address`: Loki Push Api endpoint address. Default value: `localhost`
  + `path`: Loki Push Api endpoint path. Default value: `loki/api/v1/push`

The `LokiPushApiProvider` object has several responsibilities:

1. Set the URL of the Loki Push API in the relation application data bag; the URL
   must be unique to all instances (e.g. using a load balancer).
   The default URL is the FQDN, but this can be overridden by calling `update_endpoint()`.
2. Set the Promtail binary URL (`promtail_binary_zip_url`) so clients that use
   `LogProxyConsumer` object could download and configure it.
3. Process the metadata of the consumer application, provided via the
   "metadata" field of the consumer data bag, which are used to annotate the
   alert rules (see next point). An example for "metadata" is the following:

   {'model': 'loki',
   'model\_uuid': '0b7d1071-ded2-4bf5-80a3-10a81aeb1386',
   'application': 'promtail-k8s'
   }
4. Process alert rules set into the relation by the `LokiPushApiConsumer`
   objects, e.g.:

   '{
   "groups": [{
   "name": "loki\_0b7d1071-ded2-4bf5-80a3-10a81aeb1386\_promtail-k8s\_alerts",
   "rules": [{
   "alert": "HighPercentageError",
   "expr": "sum(rate({app=\"foo\", env=\"production\"} |= \"error\" [5m]))
   by (job) \n /\nsum(rate({app=\"foo\", env=\"production\"}[5m]))
   by (job)\n > 0.05
   \n", "for": "10m",
   "labels": {
   "severity": "page",
   "juju\_model": "loki",
   "juju\_model\_uuid": "0b7d1071-ded2-4bf5-80a3-10a81aeb1386",
   "juju\_application": "promtail-k8s"
   },
   "annotations": {
   "summary": "High request latency"
   }
   }]
   }]
   }'

Once these alert rules are sent over relation data, the `LokiPushApiProvider` object
stores these files in the directory `/loki/rules` inside the Loki charm container. After
storing alert rules files, the object will check alert rules by querying Loki API
endpoint: [`loki/api/v1/rules`](https://grafana.com/docs/loki/latest/api/#list-rule-groups).
If there are changes in the alert rules a `loki_push_api_alert_rules_changed` event will
be emitted with details about the `RelationEvent` which triggered it.

This events should be observed in the charm that uses `LokiPushApiProvider`:

```
    def __init__(self, *args):
        super().__init__(*args)
        ...
        self.loki_provider = LokiPushApiProvider(self)
        self.framework.observe(
            self.loki_provider.on.loki_push_api_alert_rules_changed,
            self._loki_push_api_alert_rules_changed,
        )
```

##### LokiPushApiConsumer Library Usage

This Loki charm interacts with its clients using the Loki charm library. Charms
seeking to send log to Loki, must do so using the `LokiPushApiConsumer` object from
this charm library.

> **NOTE**: `LokiPushApiConsumer` also depends on an additional charm library.
>
> Ensure sure you `charmcraft fetch-lib charms.observability_libs.v0.juju_topology`
> when using this library.

For the simplest use cases, using the `LokiPushApiConsumer` object only requires
instantiating it, typically in the constructor of your charm (the one which
sends logs).

```
from charms.loki_k8s.v1.loki_push_api import LokiPushApiConsumer

class LokiClientCharm(CharmBase):

    def __init__(self, *args):
        super().__init__(*args)
        ...
        self._loki_consumer = LokiPushApiConsumer(self)
```

The `LokiPushApiConsumer` constructor requires two things:

* A reference to the parent (LokiClientCharm) charm.
* Optionally, the name of the relation that the Loki charm uses to interact
  with its clients. If provided, this relation name must match a required
  relation in metadata.yaml with the `loki_push_api` interface.

  If not provided, the relation name defaults to `logging`.

Any time the relation between a Loki provider charm and a Loki consumer charm is
established, a `LokiPushApiEndpointJoined` event is fired. In the consumer side
is it possible to observe this event with:

```
self.framework.observe(
    self._loki_consumer.on.loki_push_api_endpoint_joined,
    self._on_loki_push_api_endpoint_joined,
)
```

Any time there are departures in relations between the consumer charm and Loki
the consumer charm is informed, through a `LokiPushApiEndpointDeparted` event, for instance:

```
self.framework.observe(
    self._loki_consumer.on.loki_push_api_endpoint_departed,
    self._on_loki_push_api_endpoint_departed,
)
```

The consumer charm can then choose to update its configuration in both situations.

Note that LokiPushApiConsumer does not add any labels automatically on its own. In
order to better integrate with the Canonical Observability Stack, you may want to configure your
software to add Juju topology labels. The
[observability-libs](https://charmhub.io/observability-libs) library can be used to get topology
labels in charm code. See :func:`LogProxyConsumer._scrape_configs` for an example of how
to do this with promtail.

##### LogProxyConsumer Library Usage

> Note: This object is deprecated. Consider migrating to LogForwarder with the release of Juju 3.6
> LTS.

Let's say that we have a workload charm that produces logs, and we need to send those logs to a
workload implementing the `loki_push_api` interface, such as `Loki` or `Grafana Agent`.

Adopting this object in a Charmed Operator consist of two steps:

1. Use the `LogProxyConsumer` class by instantiating it in the `__init__` method of the charmed
   operator. There are two ways to get logs in to promtail. You can give it a list of files to
   read, or you can write to it using the syslog protocol.

   For example:

   ```
   from charms.loki_k8s.v1.loki_push_api import LogProxyConsumer

   ...

       def __init__(self, *args):
           ...
           self._log_proxy = LogProxyConsumer(
               self,
               logs_scheme={
                   "workload-a": {
                       "log-files": ["/tmp/worload-a-1.log", "/tmp/worload-a-2.log"],
                       "syslog-port": 1514,
                   },
                   "workload-b": {"log-files": ["/tmp/worload-b.log"], "syslog-port": 1515},
               },
               relation_name="log-proxy",
           )
           self.framework.observe(
               self._log_proxy.on.promtail_digest_error,
               self._promtail_error,
           )

       def _promtail_error(self, event):
           logger.error(event.message)
           self.unit.status = BlockedStatus(event.message)
   ```

   Any time the relation between a provider charm and a LogProxy consumer charm is
   established, a `LogProxyEndpointJoined` event is fired. In the consumer side is it
   possible to observe this event with:

   ```
   self.framework.observe(
       self._log_proxy.on.log_proxy_endpoint_joined,
       self._on_log_proxy_endpoint_joined,
   )
   ```

   Any time there are departures in relations between the consumer charm and the provider
   the consumer charm is informed, through a `LogProxyEndpointDeparted` event, for instance:

   ```
   self.framework.observe(
       self._log_proxy.on.log_proxy_endpoint_departed,
       self._on_log_proxy_endpoint_departed,
   )
   ```

   The consumer charm can then choose to update its configuration in both situations.

   Note that:

   * You can configure your syslog software using `localhost` as the address and the method
     `LogProxyConsumer.syslog_port("container_name")` to get the port, or, alternatively, if you are using rsyslog
     you may use the method `LogProxyConsumer.rsyslog_config("container_name")`.
2. Modify the `metadata.yaml` file to add:

   * The `log-proxy` relation in the `requires` section:

     ```
     requires:
       log-proxy:
         interface: loki_push_api
         optional: true
     ```

Once the library is implemented in a Charmed Operator and a relation is established with
the charm that implements the `loki_push_api` interface, the library will inject a
Pebble layer that runs Promtail in the workload container to send logs.

By default, the promtail binary injected into the container will be downloaded from the internet.
If, for any reason, the container has limited network access, you may allow charm administrators
to provide their own promtail binary at runtime by adding the following snippet to your charm
metadata:

```
resources:
  promtail-bin:
      type: file
      description: Promtail binary for logging
      filename: promtail-linux
```

Which would then allow operators to deploy the charm this way:

```
juju deploy \
    ./your_charm.charm \
    --resource promtail-bin=/tmp/promtail-linux-amd64
```

If a different resource name is used, it can be specified with the `promtail_resource_name`
argument to the `LogProxyConsumer` constructor.

The object can emit a `PromtailDigestError` event:

* Promtail binary cannot be downloaded.
* The sha256 sum mismatch for promtail binary.

The object can raise a `ContainerNotFoundError` event:

* No `container_name` parameter has been specified and the Pod has more than 1 container.

These can be monitored via the PromtailDigestError events via:

```
   self.framework.observe(
       self._loki_consumer.on.promtail_digest_error,
       self._promtail_error,
   )

   def _promtail_error(self, event):
       logger.error(msg)
       self.unit.status = BlockedStatus(event.message)
    )
```

##### LogForwarder class Usage

Let's say that we have a charm's workload that writes logs to the standard output (stdout),
and we need to send those logs to a workload implementing the `loki_push_api` interface,
such as `Loki` or `Grafana Agent`. To know how to reach a Loki instance, a charm would
typically use the `loki_push_api` interface.

Use the `LogForwarder` class by instantiating it in the `__init__` method of the charm:

```
from charms.loki_k8s.v1.loki_push_api import LogForwarder

...

  def __init__(self, *args):
      ...
      self._log_forwarder = LogForwarder(
          self,
          relation_name="logging"  # optional, defaults to `logging`
      )
```

The `LogForwarder` by default will observe relation events on the `logging` endpoint and
enable/disable log forwarding automatically.
Next, modify the `metadata.yaml` file to add:

The `log-forwarding` relation in the `requires` section:

```
requires:
 logging:
   interface: loki_push_api
   optional: true
```

Once the LogForwader class is implemented in your charm and the relation (implementing the
`loki_push_api` interface) is active and healthy, the library will inject a Pebble layer in
each workload container the charm has access to, to configure Pebble's log forwarding
feature and start sending logs to Loki.

##### Alerting Rules

This charm library also supports gathering alerting rules from all related Loki client
charms and enabling corresponding alerts within the Loki charm. Alert rules are
automatically gathered by `LokiPushApiConsumer` object from a directory conventionally
named `loki_alert_rules`.

This directory must reside at the top level in the `src` folder of the
consumer charm. Each file in this directory is assumed to be a single alert rule
in YAML format. The file name must have one of the following extensions: `.yaml`, `.yml`, `.rule`, or `.rules`.
The format of this alert rule conforms to the
[Loki docs](https://grafana.com/docs/loki/latest/rules/#alerting-rules).

An example of the contents of one such file is shown below.

```
alert: HighPercentageError
expr: |
  sum(rate({%%juju_topology%%} |= "error" [5m])) by (job)
    /
  sum(rate({%%juju_topology%%}[5m])) by (job)
    > 0.05
for: 10m
labels:
    severity: page
annotations:
    summary: High request latency
```

It is **critical** to use the `%%juju_topology%%` filter in the expression for the alert
rule shown above. This filter is a stub that is automatically replaced by the
`LokiPushApiConsumer` following Loki Client's Juju topology (application, model and its
UUID). Such a topology filter is essential to ensure that alert rules submitted by one
provider charm generates alerts only for that same charm.

The Loki charm may be related to multiple Loki client charms. Without this, filter
rules submitted by one provider charm will also result in corresponding alerts for other
provider charms. Hence, every alert rule expression must include such a topology filter stub.

Gathering alert rules and generating rule files within the Loki charm is easily done using
the `alerts()` method of `LokiPushApiProvider`. Alerts generated by Loki will automatically
include Juju topology labels in the alerts. These labels indicate the source of the alert.

The following labels are automatically added to every alert

* `juju_model`
* `juju_model_uuid`
* `juju_application`

Whether alert rules files does not contain the keys `alert` or `expr` or there is no alert
rules file in `alert_rules_path` a `loki_push_api_alert_rules_error` event is emitted.

To handle these situations the event must be observed in the `LokiClientCharm` charm.py file:

```
class LokiClientCharm(CharmBase):

    def __init__(self, *args):
        super().__init__(*args)
        ...
        self._loki_consumer = LokiPushApiConsumer(self)

        self.framework.observe(
            self._loki_consumer.on.loki_push_api_alert_rules_error,
            self._alert_rules_error
        )

    def _alert_rules_error(self, event):
        self.unit.status = BlockedStatus(event.message)
```

##### Relation Data

The Loki charm uses both application and unit relation data to obtain information regarding
Loki Push API and alert rules.

Units of consumer charm send their alert rules over app relation data using the `alert_rules`
key.

##### Alert rules encoding

The consumer publishes its alert rules to the `alert_rules` key of its application
databag. Because large deployments can produce enough alert rules to exceed Juju's
relation data size limit, the rules can be stored LZMA-compressed and base64-encoded
instead of as plain JSON.

Compression is negotiated over the relation: the provider advertises the encodings it
is able to read in the `alert_rules_encodings` key of its own application databag, and
the consumer picks the best encoding both sides support. A consumer related to a
provider running an older version of this library (which advertises nothing) keeps
writing plain JSON, so upgrades are safe in any order.

An admin can decode compressed rules with:

```
<alert-rules-from-show-unit> | base64 -d | xz -d | jq
```

##### Charm logging

The `charms.loki_k8s.v0.charm_logging` library can be used in conjunction with this one to configure python's
logging module to forward all logs to Loki via the loki-push-api interface.

```
from lib.charms.loki_k8s.v0.charm_logging import log_charm
from lib.charms.loki_k8s.v1.loki_push_api import charm_logging_config, LokiPushApiConsumer

@log_charm(logging_endpoint="my_endpoints", server_cert="cert_path")
class MyCharm(...):
    _cert_path = "/path/to/cert/on/charm/container.crt"
    def __init__(self, ...):
        self.logging = LokiPushApiConsumer(...)
        self.my_endpoints, self.cert_path = charm_logging_config(
            self.logging, self._cert_path)
```

Do this, and all charm logs will be forwarded to Loki as soon as a relation is formed.

---

Index

* [class LokiPushApiError](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapierror)
* [class RelationNotFoundError](https://charmhub.io/loki-k8s/libraries/loki_push_api#relationnotfounderror)
* + [def \_\_init\_\_(
    self,
    relation\_name)](https://charmhub.io/loki-k8s/libraries/loki_push_api#relationnotfounderror-__init__)
* [class RelationInterfaceMismatchError](https://charmhub.io/loki-k8s/libraries/loki_push_api#relationinterfacemismatcherror)
* + [def \_\_init\_\_(
    self,
    relation\_name,
    expected\_relation\_interface,
    actual\_relation\_interface)](https://charmhub.io/loki-k8s/libraries/loki_push_api#relationinterfacemismatcherror-__init__)
* [class RelationRoleMismatchError](https://charmhub.io/loki-k8s/libraries/loki_push_api#relationrolemismatcherror)
* + [def \_\_init\_\_(
    self,
    relation\_name,
    expected\_relation\_role,
    actual\_relation\_role)](https://charmhub.io/loki-k8s/libraries/loki_push_api#relationrolemismatcherror-__init__)
* [class InvalidAlertRulePathError](https://charmhub.io/loki-k8s/libraries/loki_push_api#invalidalertrulepatherror)
* + [def \_\_init\_\_(
    self,
    alert\_rules\_absolute\_path,
    message)](https://charmhub.io/loki-k8s/libraries/loki_push_api#invalidalertrulepatherror-__init__)
* [class NoRelationWithInterfaceFoundError](https://charmhub.io/loki-k8s/libraries/loki_push_api#norelationwithinterfacefounderror)
* + [def \_\_init\_\_(
    self,
    charm,
    relation\_interface)](https://charmhub.io/loki-k8s/libraries/loki_push_api#norelationwithinterfacefounderror-__init__)
* [class MultipleRelationsWithInterfaceFoundError](https://charmhub.io/loki-k8s/libraries/loki_push_api#multiplerelationswithinterfacefounderror)
* + [def \_\_init\_\_(
    self,
    charm,
    relation\_interface,
    relations)](https://charmhub.io/loki-k8s/libraries/loki_push_api#multiplerelationswithinterfacefounderror-__init__)
* [class LokiPushApiEndpointDeparted](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiendpointdeparted)
* [class LokiPushApiEndpointJoined](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiendpointjoined)
* [class LokiPushApiAlertRulesChanged](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapialertruleschanged)
* + [def \_\_init\_\_(
    self,
    handle,
    relation,
    relation\_id,
    app,
    unit)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapialertruleschanged-__init__)
* + [def snapshot(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapialertruleschanged-snapshot)
* + [def restore(
    self,
    snapshot)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapialertruleschanged-restore)
* [class InvalidAlertRuleEvent](https://charmhub.io/loki-k8s/libraries/loki_push_api#invalidalertruleevent)
* + [def \_\_init\_\_(
    self,
    handle,
    errors,
    valid)](https://charmhub.io/loki-k8s/libraries/loki_push_api#invalidalertruleevent-__init__)
* + [def snapshot(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#invalidalertruleevent-snapshot)
* + [def restore(
    self,
    snapshot)](https://charmhub.io/loki-k8s/libraries/loki_push_api#invalidalertruleevent-restore)
* [class LokiPushApiEvents](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapievents)
* [class LokiPushApiProvider](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiprovider)
* + [def \_\_init\_\_(
    self,
    charm,
    relation\_name)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiprovider-__init__)
* + [def update\_endpoint(
    self,
    url,
    relation)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiprovider-update_endpoint)
* + [def alerts(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiprovider-alerts)
* + [def has\_invalid\_alert\_rules(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiprovider-has_invalid_alert_rules)
* [class ConsumerBase](https://charmhub.io/loki-k8s/libraries/loki_push_api#consumerbase)
* + [def \_\_init\_\_(
    self,
    charm,
    relation\_name,
    alert\_rules\_path,
    recursive,
    skip\_alert\_topology\_labeling)](https://charmhub.io/loki-k8s/libraries/loki_push_api#consumerbase-__init__)
* + [def loki\_endpoints(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#consumerbase-loki_endpoints)
* [class LokiPushApiConsumer](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiconsumer)
* + [def \_\_init\_\_(
    self,
    charm,
    relation\_name,
    alert\_rules\_path,
    recursive,
    skip\_alert\_topology\_labeling)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiconsumer-__init__)
* + [def reload\_alerts(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#lokipushapiconsumer-reload_alerts)
* [class ContainerNotFoundError](https://charmhub.io/loki-k8s/libraries/loki_push_api#containernotfounderror)
* + [def \_\_init\_\_(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#containernotfounderror-__init__)
* [class PromtailDigestError](https://charmhub.io/loki-k8s/libraries/loki_push_api#promtaildigesterror)
* + [def \_\_init\_\_(
    self,
    handle,
    message)](https://charmhub.io/loki-k8s/libraries/loki_push_api#promtaildigesterror-__init__)
* + [def snapshot(
    self)](https://charmhub.io/loki-k8s/libraries/loki_push_api#promtaildigesterror-snapshot)
* + [def restore(
    self,
    snapshot)](https://charmhub.io/loki-k8s/libraries/loki_push_api#promtaildigesterror-restore)
* [class LogProxyEndpointDeparted](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyendpointdeparted)
* [class LogProxyEndpointJoined](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyendpointjoined)
* [class LogProxyEvents](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyevents)
* [class LogProxyConsumer](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyconsumer)
* + [def \_\_init\_\_(
    self,
    charm)](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyconsumer-__init__)
* + [def syslog\_port(
    self,
    container\_name)](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyconsumer-syslog_port)
* + [def rsyslog\_config(
    self,
    container\_name)](https://charmhub.io/loki-k8s/libraries/loki_push_api#logproxyconsumer-rsyslog_config)
* [class LogForwarder](https://charmhub.io/loki-k8s/libraries/loki_push_api#logforwarder)
* + [def \_\_init\_\_(
    self,
    charm)](https://charmhub.io/loki-k8s/libraries/loki_push_api#logforwarder-__init__)
* + [def is\_ready(
    self,
    relation)](https://charmhub.io/loki-k8s/libraries/loki_push_api#logforwarder-is_ready)
* [def charm\_logging\_config(
  endpoint\_requirer,
  cert\_path
  )](https://charmhub.io/loki-k8s/libraries/loki_push_api#charm_logging_config)

#### class LokiPushApiError

Description

Base class for errors raised by this module. None

#### class RelationNotFoundError

Description

Raised if there is no relation with the given name. None

Methods

RelationNotFoundError.
\_\_init\_\_(

*self*

,
relation\_name: str
)

#### class RelationInterfaceMismatchError

Description

Raised if the relation with the given name has a different interface. None

Methods

RelationInterfaceMismatchError.
\_\_init\_\_(

*self*

,
relation\_name: str

,
expected\_relation\_interface: str

,
actual\_relation\_interface: str
)

#### class RelationRoleMismatchError

Description

Raised if the relation with the given name has a different direction. None

Methods

RelationRoleMismatchError.
\_\_init\_\_(

*self*

,
relation\_name: str

,
expected\_relation\_role: RelationRole

,
actual\_relation\_role: RelationRole
)

#### class InvalidAlertRulePathError

Description

Raised if the alert rules folder cannot be found or is otherwise invalid. None

Methods

InvalidAlertRulePathError.
\_\_init\_\_(

*self*

,
alert\_rules\_absolute\_path: Path

,
message: str
)

#### class NoRelationWithInterfaceFoundError

Description

No relations with the given interface are found in the charm meta. None

Methods

NoRelationWithInterfaceFoundError.
\_\_init\_\_(

*self*

,
charm: CharmBase

,
relation\_interface
)

#### class MultipleRelationsWithInterfaceFoundError

Description

Multiple relations with the given interface are found in the charm meta. None

Methods

MultipleRelationsWithInterfaceFoundError.
\_\_init\_\_(

*self*

,
charm: CharmBase

,
relation\_interface: str

,
relations: list
)

#### class LokiPushApiEndpointDeparted

Description

Event emitted when Loki departed. None

#### class LokiPushApiEndpointJoined

Description

Event emitted when Loki joined. None

#### class LokiPushApiAlertRulesChanged

Description

Event emitted if there is a change in the alert rules. None

Methods

LokiPushApiAlertRulesChanged.
\_\_init\_\_(

*self*

,
handle

,
relation

,
relation\_id

,
app

,
unit
)

Pretend we are almost like a RelationEvent.

Description

Fields to serialize:
{
"relation\_name": <a relation name as a string>,
"relation\_id": <a relation id, optional>,
"app\_name": <app name as a string>,
"unit\_name": <unit name as a string>
}

In this way, we can transparently use `RelationEvent.snapshot()` to pass
it back if we need to log it.

LokiPushApiAlertRulesChanged.
snapshot(

*self*
)

Description

Save event information. None

LokiPushApiAlertRulesChanged.
restore(

*self*

,
snapshot: dict
)

Description

Restore event information. None

#### class InvalidAlertRuleEvent

Event emitted when alert rule files are not parsable.

Description

Enables us to set a clear status on the provider.

Methods

InvalidAlertRuleEvent.
\_\_init\_\_(

*self*

,
handle

,
errors: str

,
valid: bool
)

InvalidAlertRuleEvent.
snapshot(

*self*
)

Description

Save alert rule information. None

InvalidAlertRuleEvent.
restore(

*self*

,
snapshot
)

Description

Restore alert rule information. None

#### class LokiPushApiEvents

Description

Event descriptor for events raised by `LokiPushApiProvider`. None

#### class LokiPushApiProvider

Description

A LokiPushApiProvider class. None

Methods

LokiPushApiProvider.
\_\_init\_\_(

*self*

,
charm

,
relation\_name: str
)

A Loki service provider.

Arguments

charm

a `CharmBase` instance that manages this
instance of the Loki service.

relation\_name

an optional string name of the relation between `charm`
and the Loki charmed service. The default is "logging".
It is strongly advised not to change the default, so that people
deploying your charm will have a consistent experience with all
other charms that consume metrics endpoints.

port

an optional port of the Loki service (default is "3100").

scheme

an optional scheme of the Loki API URL (default is "http").

address

DEPRECATED. This argument is ignored and will be removed in v2.
It is kept for backward compatibility.
Use `update_endpoint()` instead.

path

an optional path of the Loki API URL (default is "loki/api/v1/push")

LokiPushApiProvider.
update\_endpoint(

*self*

,
url: str

,
relation
)

Triggers programmatically the update of endpoint in unit relation data.

Arguments

url

An optional url value to update relation data.

relation

An optional instance of `class:ops.model.Relation` to update.

Description

This method should be used when the charm relying on this library needs
to update the relation data in response to something occurring outside
the `logging` relation lifecycle, e.g., in case of a
host address change because the charmed operator becomes connected to an
Ingress after the `logging` relation is established.

To make this library reconciler-friendly, the endpoint URL was made sticky i.e., once the
endpoint is updated with a custom URL, using the public method, it cannot be unset. Users
of this method should set the "url" arg to an internal URL if the charms ingress is no
longer available.

LokiPushApiProvider.
alerts(

*self*
)

Fetch alerts for all relations.

Returns

a dictionary of alert rule groups and associated scrape
metadata indexed by relation ID.

Description

A Loki alert rules file consists of a list of "groups". Each
group consists of a list of alerts (`rules`) that are sequentially
executed. This method returns all the alert rules provided by each
related metrics provider charm. These rules may be used to generate a
separate alert rules file for each relation since the returned list
of alert groups are indexed by relation ID. Also for each relation ID
associated scrape metadata such as Juju model, UUID and application
name are provided so a unique name may be generated for the rules
file. For each relation the structure of data returned is a dictionary
with four keys

* groups
* model
* model\_uuid
* application

The value of the `groups` key is such that it may be used to generate
a Loki alert rules file directly using `yaml.dump` but the
`groups` key itself must be included as this is required by Loki,
for example as in `yaml.dump({"groups": alerts["groups"]})`.

Currently only accepts a list of rules and these
rules are all placed into a single group, even though Loki itself
allows for multiple groups within a single alert rules file.

LokiPushApiProvider.
has\_invalid\_alert\_rules(

*self*
)

Check whether any relation reported invalid alert rules.

Returns

True if any related consumer reported alert rule validation
errors, False otherwise.

Description

Validation errors, written to relation app data by the :attr:`alerts`
property, are read back to determine whether the relation currently
carries invalid alert rules. Non-leader units never write the app data
that holds these errors, so they always report no errors.

#### class ConsumerBase

Description

Consumer's base class. None

Methods

ConsumerBase.
\_\_init\_\_(

*self*

,
charm: CharmBase

,
relation\_name: str

,
alert\_rules\_path: str

,
recursive: bool

,
skip\_alert\_topology\_labeling: bool
)

ConsumerBase.
loki\_endpoints(

*self*
)

Fetch Loki Push API endpoints sent from LokiPushApiProvider through relation data.

Returns

A list of unique dictionaries with Loki Push API endpoints, for instance:
[
{"url": "http://loki1:3100/loki/api/v1/push"},
{"url": "http://loki2:3100/loki/api/v1/push"},
]

#### class LokiPushApiConsumer

Description

Loki Consumer class. None

Methods

LokiPushApiConsumer.
\_\_init\_\_(

*self*

,
charm: CharmBase

,
relation\_name: str

,
alert\_rules\_path: str

,
recursive: bool

,
skip\_alert\_topology\_labeling: bool
)

Construct a Loki charm client.

Arguments

charm

a `CharmBase` object that manages this `LokiPushApiConsumer` object.
Typically, this is `self` in the instantiating class.

relation\_name

the string name of the relation interface to look up.
If `charm` has exactly one relation with this interface, the relation's
name is returned. If none or multiple relations with the provided interface
are found, this method will raise either a NoRelationWithInterfaceFoundError or
MultipleRelationsWithInterfaceFoundError exception, respectively.

alert\_rules\_path

a string indicating a path where alert rules can be found

recursive

Whether to scan for rule files recursively.

skip\_alert\_topology\_labeling

whether to skip the alert topology labeling.

forward\_alert\_rules

a boolean flag to toggle forwarding of charmed alert rules.

extra\_alert\_labels

Dict of extra labels to inject alert rules with.

refresh\_event

an optional bound event or list of bound events which
will be observed to re-set scrape job data (IP address and others)

Description

The `LokiPushApiConsumer` object provides configurations to a Loki client charm, such as
the Loki API endpoint to push logs. It is intended for workloads that can speak
loki\_push\_api (https://grafana.com/docs/loki/latest/api/#push-log-entries-to-loki), such
as grafana-agent.
(If you need to forward workload stdout logs, then use LogForwarder; if you need to forward
log files, then use LogProxyConsumer.)

`LokiPushApiConsumer` can be instantiated as follows:

```
self._loki_consumer = LokiPushApiConsumer(self)
```

LokiPushApiConsumer.
reload\_alerts(

*self*
)

Description

Reloads alert rules and updates all relations. None

#### class ContainerNotFoundError

Description

Raised if the specified container does not exist. None

Methods

ContainerNotFoundError.
\_\_init\_\_(

*self*
)

#### class PromtailDigestError

Description

Event emitted when there is an error with Promtail initialization. None

Methods

PromtailDigestError.
\_\_init\_\_(

*self*

,
handle

,
message
)

PromtailDigestError.
snapshot(

*self*
)

Description

Save message information. None

PromtailDigestError.
restore(

*self*

,
snapshot
)

Description

Restore message information. None

#### class LogProxyEndpointDeparted

Description

Event emitted when a Log Proxy has departed. None

#### class LogProxyEndpointJoined

Description

Event emitted when a Log Proxy joins. None

#### class LogProxyEvents

Description

Event descriptor for events raised by `LogProxyConsumer`. None

#### class LogProxyConsumer

LogProxyConsumer class.

Arguments

charm

a `CharmBase` object that manages this `LokiPushApiConsumer` object.
Typically, this is `self` in the instantiating class.

logs\_scheme

a dict which maps containers and a list of log files and syslog port.

relation\_name

the string name of the relation interface to look up.
If `charm` has exactly one relation with this interface, the relation's
name is returned. If none or multiple relations with the provided interface
are found, this method will raise either a NoRelationWithInterfaceFoundError or
MultipleRelationsWithInterfaceFoundError exception, respectively.

containers\_syslog\_port

a dict which maps (and enable) containers and syslog port.

alert\_rules\_path

an optional path for the location of alert rules
files. Defaults to "./src/loki\_alert\_rules",
resolved from the directory hosting the charm entry file.
The alert rules are automatically updated on charm upgrade.

recursive

Whether to scan for rule files recursively.

promtail\_resource\_name

An optional promtail resource name from metadata
if it has been modified and attached

insecure\_skip\_verify

skip SSL verification.

Description

> Note: This object is deprecated. Consider migrating to LogForwarder with the release of Juju
> 3.6 LTS.

The `LogProxyConsumer` object provides a method for attaching `promtail` to
a workload in order to generate structured logging data from applications
which traditionally log to syslog or do not have native Loki integration.
The `LogProxyConsumer` can be instantiated as follows:

```
self._log_proxy = LogProxyConsumer(
    self,
    logs_scheme={
        "workload-a": {
            "log-files": ["/tmp/worload-a-1.log", "/tmp/worload-a-2.log"],
            "syslog-port": 1514,
        },
        "workload-b": {"log-files": ["/tmp/worload-b.log"], "syslog-port": 1515},
    },
    relation_name="log-proxy",
)
```

Methods

LogProxyConsumer.
\_\_init\_\_(

*self*

,
charm
)

LogProxyConsumer.
syslog\_port(

*self*

,
container\_name: str
)

Gets the port on which promtail is listening for syslog in this container.

Returns

A str representing the port

LogProxyConsumer.
rsyslog\_config(

*self*

,
container\_name: str
)

Generates a config line for use with rsyslog.

Returns

The rsyslog config line as a string

#### class LogForwarder

Forward the standard outputs of all workloads operated by a charm to one or multiple Loki endpoints.

Description

This class implements Pebble log forwarding. Juju >= 3.4 is needed.

Methods

LogForwarder.
\_\_init\_\_(

*self*

,
charm: CharmBase
)

LogForwarder.
is\_ready(

*self*

,
relation
)

Description

Check if the relation is active and healthy. None

#### def charm\_logging\_config(     endpoint\_requirer: LokiPushApiConsumer,     cert\_path )

Utility function to determine the charm\_logging config you will likely want.

Arguments

endpoint\_requirer

an instance of LokiPushApiConsumer.

cert\_path

a path where a cert is stored.

Returns

A tuple with (optionally) the values of the endpoints and the certificate path.

Description

If no endpoint is provided:
disable charm logging.
If https endpoint is provided but cert\_path is not found on disk:
disable charm logging.
If https endpoint is provided and cert\_path is None:
ERROR
Else:
proceed with charm logging (with or without tls, as appropriate)
