""" Jaeger tracing utilities using OpenTelemetry. Uses OpenTelemetry OTLP HTTP exporter to send traces to Jaeger. This matches the Go API approach of using HTTP instead of unreliable UDP. """ import logging import os from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.requests import RequestsInstrumentor logger = logging.getLogger(__name__) def is_tracing_enabled() -> bool: """ Check if tracing is enabled via ENABLE_TRACING environment variable. Returns: True if tracing is enabled (default), False otherwise """ return os.getenv("ENABLE_TRACING", "true").lower() in ("true", "1", "yes") def init_tracer(service_name: str) -> trace.Tracer: """ Initialize OpenTelemetry tracer with OTLP HTTP exporter for Jaeger. Uses HTTP protocol like the Go API (instead of unreliable UDP). Jaeger >= 1.35 supports OTLP natively on port 4318. Can be disabled by setting ENABLE_TRACING=false environment variable. Args: service_name: Name of the service for tracing Returns: Configured tracer instance (or no-op tracer if disabled) """ # Check if tracing is enabled BEFORE doing anything if not is_tracing_enabled(): print(f"[TRACING] Tracing disabled via ENABLE_TRACING environment variable") logger.info("OpenTelemetry tracing disabled - no exporter or instrumentation will be initialized") # Return no-op tracer WITHOUT setting up any exporters or instrumentation # This prevents any connection attempts to Jaeger return trace.get_tracer(service_name) # Jaeger OTLP HTTP endpoint (port 4318) otlp_endpoint = os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://jaeger:4318") sampling_rate = float(os.getenv("JAEGER_SAMPLING_RATE", "1.0")) print(f"[TRACING] Initializing OpenTelemetry with endpoint={otlp_endpoint}") # Create resource with service name resource = Resource.create({"service.name": service_name}) # Create tracer provider provider = TracerProvider(resource=resource) # Create OTLP HTTP exporter otlp_exporter = OTLPSpanExporter( endpoint=f"{otlp_endpoint}/v1/traces", timeout=2, # 2 second timeout like Go API ) # Add batch span processor (matches Go API's buffering) processor = BatchSpanProcessor( otlp_exporter, max_queue_size=2048, # Default queue size max_export_batch_size=512, # Must be <= max_queue_size schedule_delay_millis=1000, # 1 second flush interval like Go API ) provider.add_span_processor(processor) # Set as global tracer provider trace.set_tracer_provider(provider) # Get tracer tracer = trace.get_tracer(service_name) # Auto-instrument Flask and requests ONLY when tracing is enabled FlaskInstrumentor().instrument() RequestsInstrumentor().instrument() print(f"[TRACING] OpenTelemetry tracer initialized: service={service_name}, endpoint={otlp_endpoint}, sampling={sampling_rate}") logger.info( f"OpenTelemetry tracer initialized with OTLP HTTP exporter", extra={ "service": service_name, "otlp_endpoint": otlp_endpoint, "sampling_rate": sampling_rate, } ) return tracer