Loki

  • Canonical Observability
Channel Revision Published Runs on
latest/stable 160 10 Sep 2024
Ubuntu 20.04
latest/candidate 161 19 Nov 2024
Ubuntu 20.04
latest/beta 177 19 Nov 2024
Ubuntu 20.04
latest/edge 177 16 Nov 2024
Ubuntu 20.04
1.0/stable 104 12 Dec 2023
Ubuntu 20.04
1.0/candidate 104 22 Nov 2023
Ubuntu 20.04
1.0/beta 104 22 Nov 2023
Ubuntu 20.04
1.0/edge 104 22 Nov 2023
Ubuntu 20.04
juju deploy loki-k8s --channel edge
Show information

Platform:

charms.loki_k8s.v1.loki_push_api

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,
                address=external_url.hostname or self.hostname,
                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).

  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. 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.

    This argument is not required if your metadata.yaml has precisely one required relation in metadata.yaml with the loki_push_api interface, as the lib will automatically resolve the relation name inspecting the using the meta information of the charm

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 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 the .rule extension. The format of this alert rule conforms to the Loki docs.

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.

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

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 AlertRules

Utility class for amalgamating Loki alert rule files and injecting juju topology.

Description

An AlertRules object supports aggregating alert rules from files and directories in both official and single rule file formats using the add_path() method. All the alert rules read are annotated with Juju topology labels and amalgamated into a single data structure in the form of a Python dictionary using the as_dict() method. Such a dictionary can be easily dumped into JSON format and exchanged over relation data. The dictionary can also be dumped into YAML format and written directly into an alert rules file that is read by Loki. Note that multiple AlertRules objects must not be written into the same file, since Loki allows only a single list of alert rule groups per alert rules file.

The official Loki format is a YAML file conforming to the Loki documentation (https://grafana.com/docs/loki/latest/api/#list-rule-groups). The custom single rule format is a subsection of the official YAML, having a single alert rule, effectively "one alert per file".

Methods

AlertRules. __init__( self , topology )

Build and alert rule object.

Arguments

topology

a JujuTopology instance that is used to annotate all alert rules.

AlertRules. add_path( self , path_str: str )

Add rules from a dir path.

Arguments

path_str

either a rules file or a dir of rules files.

recursive

whether to read files recursively or not (no impact if path is a file).

Description

All rules from files are aggregated into a data structure representing a single rule file. All group names are augmented with juju topology.

AlertRules. as_dict( self )

Return standard alert rules file in dict representation.

Returns

a dictionary containing a single list of alert rule groups. The list of alert rule groups is provided as value of the "groups" dictionary key.

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

an optional address of the Loki service (default is "localhost").

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.

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.

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 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.

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)

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

class CosTool

Description

Uses cos-tool to inject label matchers into alert rule expressions and validate rules. None

Methods

CosTool. __init__( self , charm )

CosTool. path( self )

Description

Lazy lookup of the path of cos-tool. None

CosTool. apply_label_matchers( self , rules )

Description

Will apply label matchers to the expression of all alerts in all supplied groups. None

CosTool. validate_alert_rules( self , rules: dict )

Description

Will validate correctness of alert rules, returning a boolean and any errors. None

CosTool. inject_label_matchers( self , expression , topology )

Description

Add label matchers to an expression. 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)