Skip to content

Tracing API Reference

Optional OpenTelemetry tracing integration (istos[otel]).

OpenTelemetry tracing integration (optional dependency).

TracingMiddleware

Creates an OpenTelemetry span per request, linked across hops.

Continues the caller's trace when the request carried a W3C traceparent (see :class:~istos.context.RequestEnvelope), and writes this hop's active traceparent back onto the request context so any outbound query/publish propagates it to the next hop — end-to-end distributed tracing over Zenoh.

Source code in src/istos/observability/tracing.py
class TracingMiddleware:
    """Creates an OpenTelemetry span per request, linked across hops.

    Continues the caller's trace when the request carried a W3C ``traceparent``
    (see :class:`~istos.context.RequestEnvelope`), and writes this hop's active
    ``traceparent`` back onto the request context so any outbound query/publish
    propagates it to the next hop — end-to-end distributed tracing over Zenoh.
    """

    async def __call__(
        self,
        scope: RequestScope,
        call_next: HandlerCallable,
    ) -> Any:
        if _tracer is None:
            return await call_next(scope)

        from opentelemetry.trace.propagation.tracecontext import (
            TraceContextTextMapPropagator,
        )

        from istos.context import get_request_context

        propagator = TraceContextTextMapPropagator()
        incoming = scope.context.traceparent
        parent = propagator.extract({"traceparent": incoming}) if incoming else None

        with _tracer.start_as_current_span(
            f"istos.{scope.operation}",
            context=parent,
            attributes={
                "istos.prefix": scope.prefix,
                "istos.operation": scope.operation,
                "istos.correlation_id": scope.context.correlation_id,
            },
        ):
            # Publish this span as the traceparent for downstream hops.
            carrier: dict[str, str] = {}
            propagator.inject(carrier)
            tp = carrier.get("traceparent")
            if tp:
                scope.context.traceparent = tp
                get_request_context().traceparent = tp
            return await call_next(scope)

configure_tracing(service_name='istos', endpoint=None)

Configure OpenTelemetry tracing. Returns True if configured, False if opentelemetry is not installed.

Source code in src/istos/observability/tracing.py
def configure_tracing(
    service_name: str = "istos",
    endpoint: Optional[str] = None,
) -> bool:
    """
    Configure OpenTelemetry tracing. Returns True if configured, False if
    opentelemetry is not installed.
    """
    global _tracer
    try:
        from opentelemetry import trace
        from opentelemetry.sdk.trace import TracerProvider
        from opentelemetry.sdk.resources import Resource
        from opentelemetry.sdk.trace.export import BatchSpanProcessor
        from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
    except ImportError:
        return False

    resource = Resource.create({"service.name": service_name})
    provider = TracerProvider(resource=resource)
    if endpoint:
        exporter = OTLPSpanExporter(endpoint=endpoint)
        provider.add_span_processor(BatchSpanProcessor(exporter))
    trace.set_tracer_provider(provider)
    _tracer = trace.get_tracer("istos")
    return True