"""OpenTelemetry tracing and metrics initialization.""" import os from typing import Any from opentelemetry import metrics, trace from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor from opentelemetry.instrumentation.redis import RedisInstrumentor from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( MetricExporter, PeriodicExportingMetricReader, ) from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter def init_opentelemetry( service_name: str, service_version: str = "1.0.0", otlp_endpoint: str | None = None, ) -> tuple[Any, Any]: """Initialize OpenTelemetry tracing and metrics""" # Create resource resource = Resource.create( { "service.name": service_name, "service.version": service_version, "service.instance.id": os.getenv("HOSTNAME", "unknown"), } ) # Configure tracing span_exporter: SpanExporter if otlp_endpoint: span_exporter = OTLPSpanExporter(endpoint=otlp_endpoint) span_processor = BatchSpanProcessor(span_exporter) else: # Use console exporter for development try: # pylint: disable=import-outside-toplevel from opentelemetry.sdk.trace.export import ConsoleSpanExporter span_exporter = ConsoleSpanExporter() except ImportError: # Fallback to logging exporter # pylint: disable=import-outside-toplevel from opentelemetry.sdk.trace.export import ConsoleSpanExporter span_exporter = ConsoleSpanExporter() span_processor = BatchSpanProcessor(span_exporter) tracer_provider = TracerProvider(resource=resource) tracer_provider.add_span_processor(span_processor) trace.set_tracer_provider(tracer_provider) # Configure metrics metric_exporter: MetricExporter if otlp_endpoint: metric_exporter = OTLPMetricExporter(endpoint=otlp_endpoint) metric_reader = PeriodicExportingMetricReader( metric_exporter, export_interval_millis=30000 ) else: # Use console exporter for development try: # pylint: disable=import-outside-toplevel from opentelemetry.sdk.metrics.export import ConsoleMetricExporter metric_exporter = ConsoleMetricExporter() except ImportError: # Fallback to logging exporter from opentelemetry.sdk.metrics.export import ConsoleMetricExporter metric_exporter = ConsoleMetricExporter() metric_reader = PeriodicExportingMetricReader( metric_exporter, export_interval_millis=30000 ) meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader]) metrics.set_meter_provider(meter_provider) # Auto-instrument common libraries try: FastAPIInstrumentor().instrument() HTTPXClientInstrumentor().instrument() Psycopg2Instrumentor().instrument() RedisInstrumentor().instrument() except Exception: # pylint: disable=broad-exception-caught # Ignore instrumentation errors in tests pass return trace.get_tracer(service_name), metrics.get_meter(service_name)