tracing¶
Provide and consume tracing data using the tracing interface.
This document explains how to integrate with the Tempo charm for the purpose of pushing traces to a tracing endpoint provided by Tempo. It also explains how alternative implementations of the Tempo charm may maintain the same interface and be backward compatible with all currently integrated charms.
Requirer Library Usage¶
Charms seeking to push traces to Tempo, must do so using the TracingEndpointRequirer
object from this charm library. For the simplest use cases, using the TracingEndpointRequirer
object only requires instantiating it, typically in the constructor of your charm. The
TracingEndpointRequirer constructor requires the name of the relation over which a tracing
endpoint
is exposed by the Tempo charm, and a list of protocols it intends to send traces with.
This relation must use the tracing interface.
The TracingEndpointRequirer object may be instantiated as follows:
from charmlibs.interfaces.tracing import TracingEndpointRequirer
def __init__(self, *args):
super().__init__(*args)
# ...
self.tracing = TracingEndpointRequirer(self,
protocols=['otlp_grpc', 'otlp_http', 'jaeger_http_thrift']
)
# ...
Note that the first argument (self) to TracingEndpointRequirer is always a reference to the
parent charm.
Alternatively to providing the list of requested protocols at init time, the charm can do it at
any point in time by calling the
TracingEndpointRequirer.request_protocols(*protocol:str, relation:Optional[Relation]) method.
Using this method also allows you to use per-relation protocols.
Units of requirer charms obtain the tempo endpoint to which they will push their traces by calling
TracingEndpointRequirer.get_endpoint(protocol: str), where protocol is, for example:
otlp_grpcotlp_httpzipkintempo
If the protocol is not in the list of protocols that the charm requested at endpoint set-up
time,
the library will raise an error.
We recommend that you scale up your tracing provider and relate it to an ingress so that your tracing requests go through the ingress and get load balanced across all units. Otherwise, if the provider’s leader goes down, your tracing goes down.
Provider Library Usage¶
The TracingEndpointProvider object may be used by charms to manage relations with their
trace sources. For this purposes a Tempo-like charm needs to do two things:
1. Instantiate the TracingEndpointProvider object by providing it a
reference to the parent (Tempo) charm and optionally the name of the relation that the Tempo charm
uses to interact with its trace sources. This relation must conform to the tracing interface
and it is strongly recommended that this relation be named tracing which is its
default value.
For example a Tempo charm may instantiate the TracingEndpointProvider in its constructor as
follows:
from charmlibs.interfaces.tracing import TracingEndpointProvider
def __init__(self, *args):
super().__init__(*args)
# ...
self.tracing = TracingEndpointProvider(self)
# ...
- exception AmbiguousRelationUsageError¶
Bases:
TracingErrorRaised when one wrongly assumes that there can only be one relation on an endpoint.
- class BrokenEvent( )¶
Bases:
RelationBrokenEventEvent emitted when a relation on tracing is broken.
- exception DataAccessPermissionError¶
Bases:
TracingErrorRaised when follower units attempt leader-only operations.
- exception DataValidationError¶
Bases:
TracingErrorRaised when data validation fails on IPU relation data.
- class DatabagModel¶
Bases:
BaseModelBase databag model.
- classmethod load(databag: MutableMapping)¶
Load this model from a Juju databag.
- dump(
- databag: MutableMapping | None = None,
- clear: bool = True,
Write the contents of this model to Juju databag.
- Parameters:
databag – the databag to write the data to.
clear – ensure the databag is cleared before writing it.
- class EndpointChangedEvent(handle, relation, *args, **kwargs)¶
Bases:
_AutoSnapshotEventEvent representing a change in one of the receiver endpoints.
- class EndpointRemovedEvent( )¶
Bases:
RelationBrokenEventEvent representing a change in one of the receiver endpoints.
- exception NotReadyError¶
Bases:
TracingErrorRaised by the provider wrapper if a requirer hasn’t published the required data (yet).
- exception ProtocolNotRequestedError¶
Bases:
TracingErrorRaised if the user attempts to obtain an endpoint for a protocol it did not request.
- class ProtocolType(*, name: str, type: TransportProtocolType)¶
Bases:
BaseModelProtocol Type.
- type: TransportProtocolType¶
- class Receiver(*, protocol: ProtocolType, url: str)¶
Bases:
BaseModelSpecification of an active receiver.
- protocol: ProtocolType¶
- exception RelationInterfaceMismatchError( )¶
Bases:
ExceptionRaised if the relation with the given name has an unexpected interface.
- exception RelationNotFoundError(relation_name: str)¶
Bases:
ExceptionRaised if no relation with the given name is found.
- exception RelationRoleMismatchError(
- relation_name: str,
- expected_relation_role: RelationRole,
- actual_relation_role: RelationRole,
Bases:
ExceptionRaised if the relation with the given name has a different role than expected.
- class RequestEvent( )¶
Bases:
RelationEventEvent emitted when a remote requests a tracing endpoint.
- class TracingEndpointProvider( )¶
Bases:
ObjectClass representing a trace receiver service.
- on¶
TracingEndpointProvider events.
- is_requirer_ready(relation: Relation)¶
Attempt to determine if requirer has already populated app data.
- requested_protocols()¶
All receiver protocols that have been requested by our related apps.
- class TracingEndpointProviderEvents( )¶
Bases:
CharmEventsTracingEndpointProvider events.
- request¶
EventSource wraps an event type with a descriptor to facilitate observing and emitting.
It is generally used as:
class SomethingHappened(ops.EventBase): pass class SomeObject(Object): something_happened = ops.EventSource(SomethingHappened)
With that, instances of that type will offer the
someobj.something_happenedattribute which is aBoundEvent, and may be used to emit and observe the event.
- broken¶
EventSource wraps an event type with a descriptor to facilitate observing and emitting.
It is generally used as:
class SomethingHappened(ops.EventBase): pass class SomeObject(Object): something_happened = ops.EventSource(SomethingHappened)
With that, instances of that type will offer the
someobj.something_happenedattribute which is aBoundEvent, and may be used to emit and observe the event.
- class TracingEndpointRequirer(
- charm: CharmBase,
- relation_name: str = 'tracing',
- protocols: list[Literal['zipkin', 'otlp_grpc', 'otlp_http', 'jaeger_grpc', 'jaeger_thrift_http']] | None = None,
Bases:
ObjectA tracing endpoint for Tempo.
- on¶
TracingEndpointRequirer events.
- request_protocols(
- protocols: Sequence[Literal['zipkin', 'otlp_grpc', 'otlp_http', 'jaeger_grpc', 'jaeger_thrift_http']],
- relation: Relation | None = None,
Publish the list of protocols which the provider should activate.
- get_all_endpoints( ) TracingProviderAppData | None¶
Unmarshalled relation data.
- get_endpoint(
- protocol: Literal['zipkin', 'otlp_grpc', 'otlp_http', 'jaeger_grpc', 'jaeger_thrift_http'],
- relation: Relation | None = None,
Receiver endpoint for the given protocol.
It could happen that this function gets called before the provider publishes the endpoints. In such a scenario, if a non-leader unit calls this function, a permission denied exception will be raised due to restricted access. To prevent this, this function needs to be guarded by the
is_readycheck.- Raises:
ProtocolNotRequestedError – If the charm unit is the leader unit and attempts to obtain an endpoint for a protocol it did not request.
- class TracingEndpointRequirerEvents( )¶
Bases:
CharmEventsTracingEndpointRequirer events.
- endpoint_changed¶
EventSource wraps an event type with a descriptor to facilitate observing and emitting.
It is generally used as:
class SomethingHappened(ops.EventBase): pass class SomeObject(Object): something_happened = ops.EventSource(SomethingHappened)
With that, instances of that type will offer the
someobj.something_happenedattribute which is aBoundEvent, and may be used to emit and observe the event.
- endpoint_removed¶
EventSource wraps an event type with a descriptor to facilitate observing and emitting.
It is generally used as:
class SomethingHappened(ops.EventBase): pass class SomeObject(Object): something_happened = ops.EventSource(SomethingHappened)
With that, instances of that type will offer the
someobj.something_happenedattribute which is aBoundEvent, and may be used to emit and observe the event.
- class TracingProviderAppData(*, receivers: list[Receiver])¶
Bases:
DatabagModelApplication databag model for the tracing provider.
- class TracingRequirerAppData(
- *,
- receivers: list[Literal['zipkin', 'otlp_grpc', 'otlp_http', 'jaeger_grpc', 'jaeger_thrift_http']],
Bases:
DatabagModelApplication databag model for the tracing requirer.
- class TransportProtocolType(
- value,
- names=<not given>,
- *values,
- module=None,
- qualname=None,
- type=None,
- start=1,
- boundary=None,
-
Receiver Type.
- http = 'http'¶
- grpc = 'grpc'¶
- charm_tracing_config(
- endpoint_requirer: TracingEndpointRequirer,
- cert_path: Path | str | None,
Return the charm_tracing config you likely want.
If no endpoint is provided: disable charm tracing.
If https endpoint is provided but cert_path is not found on disk: disable charm tracing.
If https endpoint is provided and cert_path is None: ERROR
Else: proceed with charm tracing (with or without tls, as appropriate)
Usage:
>>> from lib.charms.tempo_coordinator_k8s.v0.charm_tracing import trace_charm >>> from charmlibs.interfaces.tracing import charm_tracing_config >>> @trace_charm(tracing_endpoint="my_endpoint", cert_path="cert_path") >>> class MyCharm(...): >>> _cert_path = "/path/to/cert/on/charm/container.crt" >>> def __init__(self, ...): >>> self.tracing = TracingEndpointRequirer(...) >>> self.my_endpoint, self.cert_path = charm_tracing_config( ... self.tracing, self._cert_path)