import os from fastapi import FastAPI from opentelemetry import metrics, trace from opentelemetry.exporter.prometheus import PrometheusMetricReader from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from opentelemetry.instrumentation.logging import LoggingInstrumentor from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.resources import SERVICE_NAME, Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import ( BatchSpanProcessor, ConsoleSpanExporter, ) from prometheus_client import start_http_server _observability_initialized = False def setup_observability(app: FastAPI) -> None: global _observability_initialized if _observability_initialized: return service_name = os.getenv("OTEL_SERVICE_NAME", "language-learning-api") metrics_host = os.getenv("OTEL_EXPORTER_PROMETHEUS_HOST", "0.0.0.0") metrics_port = int(os.getenv("OTEL_EXPORTER_PROMETHEUS_PORT", "9464")) resource = Resource.create({SERVICE_NAME: service_name}) tracer_provider = TracerProvider(resource=resource) trace.set_tracer_provider(tracer_provider) tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter())) metric_reader = PrometheusMetricReader() meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader]) metrics.set_meter_provider(meter_provider) LoggingInstrumentor().instrument(set_logging_format=True) # Expose OTel metrics for Prometheus scraping on the standard endpoint. start_http_server(port=metrics_port, addr=metrics_host) _observability_initialized = True