import time import random from flask import Flask from opentelemetry import trace, metrics from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter import logging from opentelemetry.trace import get_current_span # === Resource === resource = Resource(attributes={"service.name": "my-flask-app"}) # === Tracing === trace.set_tracer_provider(TracerProvider(resource=resource)) tracer = trace.get_tracer(__name__) # trace_exporter = OTLPSpanExporter( # endpoint="http://otel-collector.meta.svc:4318/v1/traces" # ) trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(trace_exporter)) # === Metrics === reader = PeriodicExportingMetricReader( OTLPMetricExporter(endpoint="http://otel-collector.meta.svc:4318/v1/metrics") ) provider = MeterProvider(resource=resource, metric_readers=[reader]) metrics.set_meter_provider(provider) # === Logging with trace ID === class TraceIdFilter(logging.Filter): def filter(self, record): span = trace.get_current_span() ctx = span.get_span_context() record.trace_id = format(ctx.trace_id, "032x") if ctx.trace_id != 0 else None record.span_id = format(ctx.span_id, "016x") if ctx.span_id != 0 else None return True formatter = logging.Formatter( '{"message": "%(message)s", "trace_id": "%(trace_id)s", "span_id": "%(span_id)s"}' ) handler = logging.StreamHandler() handler.setFormatter(formatter) handler.addFilter(TraceIdFilter()) logger = logging.getLogger("myapp") logger.setLevel(logging.INFO) logger.addHandler(handler) # === Flask App === app = Flask(__name__) # FlaskInstrumentor().instrument_app(app) # auto-instruments metrics + traces @app.route("/") def root(): with tracer.start_as_current_span("handle_homepage"): logger.info( "handling request", extra={"trace_id": get_current_span().get_span_context().trace_id}, ) time.sleep(random.random() * 0.05) # simulate request parsing fetch_user() calculate_recommendations() return "Hello from / with traces and metrics!" def fetch_user(): with tracer.start_as_current_span("fetch_user"): time.sleep(random.random() * 0.1) # simulate DB query def calculate_recommendations(): with tracer.start_as_current_span("calculate_recommendations"): for i in range(2): with tracer.start_as_current_span(f"score_item_{i}"): time.sleep(random.random()) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)