from typing import Optional, Sequence from aio_pika.abc import AbstractChannel from opentelemetry.context import Context from opentelemetry.sdk.trace.sampling import Sampler, SamplingResult, Decision from opentelemetry.semconv.attributes.network_attributes import NETWORK_PEER_ADDRESS, NETWORK_PEER_PORT from opentelemetry.trace import SpanKind, Link, TraceState from opentelemetry.util.types import Attributes def patch_spanbuilder_set_channel() -> None: """ The default SpanBuilder.set_channel does not work with aio_pika 9.1 and the refactored connection attribute """ import opentelemetry.instrumentation.aio_pika.span_builder from opentelemetry.instrumentation.aio_pika.span_builder import SpanBuilder def set_channel(self: SpanBuilder, channel: AbstractChannel) -> None: if hasattr(channel, "_connection"): url = channel._connection.url port = url.port or 5672 self._attributes.update( { NETWORK_PEER_ADDRESS: url.host, NETWORK_PEER_PORT: port, } ) opentelemetry.instrumentation.aio_pika.span_builder.SpanBuilder.set_channel = set_channel # type: ignore[misc] class ProgressSpanSampler(Sampler): """ Sampler which omits aio_pika messages destined/related to progress. To debug which spans are being dropped, set environment variable: OTEL_DEBUG_SAMPLER=1 """ def __init__(self): import logging import os self.logger = logging.getLogger(__name__) self.debug = os.environ.get("OTEL_DEBUG_SAMPLER", "0") == "1" def get_description(self) -> str: return "Sampler which omits aio_pika messages destined/related to progress" def should_sample( self, parent_context: Optional["Context"], trace_id: int, name: str, kind: Optional[SpanKind] = None, attributes: Attributes = None, links: Optional[Sequence["Link"]] = None, trace_state: Optional["TraceState"] = None, ) -> "SamplingResult": should_drop = False drop_reason = None if attributes is not None and "messaging.destination" in attributes and attributes["messaging.destination"].endswith("progress"): should_drop = True drop_reason = f"messaging.destination={attributes['messaging.destination']}" # the ephemeral reply channels are not required for correct span correlation elif name.startswith(",amq_") or name.startswith("amq"): should_drop = True drop_reason = f"span name starts with amq: {name}" if should_drop: if self.debug: self.logger.debug(f"[SAMPLER] DROPPED span '{name}' (kind={kind}): {drop_reason}") return SamplingResult(Decision.DROP) if self.debug: self.logger.debug(f"[SAMPLER] RECORDING span '{name}' (kind={kind}, attributes={attributes})") return SamplingResult(Decision.RECORD_AND_SAMPLE)