sloth¶
Sloth Provider and Requirer Library.
This library provides a way for charms to share SLO (Service Level Objective) specifications with the Sloth charm, which will convert them into Prometheus recording and alerting rules.
Getting Started¶
Provider Side (Charms providing SLO specs)¶
To provide SLO specifications to Sloth, use the SlothProvider class.
The recommended approach is to allow users to configure SLOs via juju config:
from charmlibs.interfaces.sloth import SlothProvider
class MyCharm(ops.CharmBase):
def __init__(self, *args):
super().__init__(*args)
self.sloth_provider = SlothProvider(self)
self.framework.observe(self.on.config_changed, self._on_config_changed)
def _on_config_changed(self, event):
# Read SLO configuration from juju config
slo_config = self.config.get('slo_config', '')
if slo_config:
self.sloth_provider.provide_slos(slo_config)
Users can then configure SLOs using juju config:
juju config my-app slo_config='
version: prometheus/v1
service: my-service
labels:
team: my-team
slos:
- name: requests-availability
objective: 99.9
description: "99.9% of requests should succeed"
sli:
events:
error_query: '\''sum(rate(http_requests_total{status=~"5.."}[{{.window}}]))'\''
total_query: '\''sum(rate(http_requests_total[{{.window}}]))'\''
alerting:
name: MyServiceHighErrorRate
labels:
severity: critical
'
To specify multiple SLOs for different services, separate them with YAML document
separators (---).
Requirer Side (Sloth charm)¶
The Sloth charm uses SlothRequirer to collect SLO specifications.
Validation is performed on the requirer side:
from charmlibs.interfaces.sloth import SlothRequirer
class SlothCharm(ops.CharmBase):
def __init__(self, *args):
super().__init__(*args)
self.sloth_requirer = SlothRequirer(self)
def _on_config_changed(self, event):
# Get validated SLO specs from all related charms
slos = self.sloth_requirer.get_slos()
# Process SLOs and generate rules
Relation Data Format¶
SLO specifications are stored in the relation databag as YAML strings under the
slo_spec key. Each provider unit can provide one or more SLO specifications.
For a single service:
slo_spec: |
version: prometheus/v1
service: my-service
labels:
team: my-team
slos:
- name: requests-availability
objective: 99.9
description: "99.9% of requests should succeed"
sli:
events:
error_query: 'sum(rate(http_requests_total{status=~"5.."}[{{.window}}]))'
total_query: 'sum(rate(http_requests_total[{{.window}}]))'
alerting:
name: MyServiceHighErrorRate
labels:
severity: critical
For multiple services (separated by YAML document separators):
slo_spec: |
version: prometheus/v1
service: my-service
slos:
- name: requests-availability
objective: 99.9
---
version: prometheus/v1
service: my-other-service
slos:
- name: requests-latency
objective: 99.5
- class SLORelationData(*, slos: list[SLOSpec] = <factory>)¶
Bases:
BaseModelPydantic model for SLO relation data exchange.
This model represents the data stored in the relation databag for exchanging SLO specifications between provider and requirer charms.
- class SLOSpec(*, version: str, service: str, labels: dict[str, str] = <factory>, slos: list[dict[str, ~typing.Any]])¶
Bases:
BaseModelPydantic model for SLO specification validation.
- class SlothProvider( )¶
Bases:
ObjectProvider side of the Sloth relation.
Charms should use this class to provide SLO specifications to Sloth.
- Parameters:
charm – The charm instance.
relation_name – Name of the relation (default: “sloth”).
inject_topology – Whether to automatically inject Juju topology labels into Prometheus queries (default: True). When enabled, labels like juju_application, juju_model, etc. are added to metric selectors.
- provide_slos(slo_config: str) None¶
Provide SLO specifications to Sloth as a raw YAML string.
This method accepts a raw YAML string containing one or more SLO specifications. Multiple specs should be separated by YAML document separators (three dashes). The YAML is validated against the SLOSpec schema before being sent.
- Parameters:
slo_config – Raw YAML string containing SLO specification(s) in Sloth format. Can contain multiple documents separated by three dashes.
- Raises:
SLOValidationError – If the YAML cannot be parsed or is invalid.
Example:
slo_config = ''' version: prometheus/v1 service: my-service labels: team: my-team slos: - name: requests-availability objective: 99.9 sli: events: error_query: 'sum(rate(http_requests_total{status=~"5.."}[{{.window}}]))' total_query: 'sum(rate(http_requests_total[{{.window}}]))' ''' self.slo_provider.provide_slos(slo_config)
- class SlothRequirer(charm: CharmBase, relation_name: str = 'sloth')¶
Bases:
ObjectRequirer side of the Sloth relation.
The Sloth charm uses this class to collect SLO specifications from related charms. Validation of SLO specs is performed on this side.
- Parameters:
charm – The charm instance.
relation_name – Name of the relation (default: “sloth”).
- inject_topology_labels(query: str, topology: dict[str, str]) str¶
Inject Juju topology labels into a Prometheus query.
This function adds Juju topology labels (juju_application, juju_model, etc.) to all metric selectors in a PromQL query that don’t already have them.
Only metrics with explicit selectors (either {labels} or [time]) are modified. Function names like sum(), rate(), etc. are not modified.
- Parameters:
query – The Prometheus query string
topology – Dictionary of label names to values (e.g., {“juju_application”: “my-app”})
- Returns:
Query with topology labels injected
Examples
>>> inject_topology_labels( ... 'sum(rate(metric[5m]))', ... {"juju_application": "my-app"} ... ) 'sum(rate(metric{juju_application="my-app"}[5m]))'
>>> inject_topology_labels( ... 'sum(rate(metric{existing="label"}[5m]))', ... {"juju_application": "my-app"} ... ) 'sum(rate(metric{existing="label",juju_application="my-app"}[5m]))'