apiVersion: v1 kind: ConfigMap metadata: name: my-python-app namespace: prod data: main.py: | 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.instrumentation.flask import FlaskInstrumentor 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__) # === 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) --- apiVersion: apps/v1 kind: Deployment metadata: name: python-otel namespace: prod labels: app: python-otel spec: replicas: 1 selector: matchLabels: app: python-otel template: metadata: labels: app: python-otel spec: containers: - name: python image: python command: ["/bin/sh", "-c"] args: - | python -m venv .venv && \ .venv/bin/python -m pip install --upgrade pip && \ .venv/bin/python -m pip install \ opentelemetry-sdk \ opentelemetry-exporter-otlp \ opentelemetry-instrumentation-flask \ flask && \ .venv/bin/python /app/main.py volumeMounts: - name: app-code mountPath: /app env: - name: OTEL_EXPORTER_OTLP_ENDPOINT value: http://otel-collector.meta.svc:4318 - name: OTEL_EXPORTER_OTLP_INSECURE value: "true" volumes: - name: app-code configMap: name: my-python-app --- apiVersion: v1 kind: Service metadata: name: python-otel namespace: prod spec: selector: app: python-otel ports: - port: 80 targetPort: 5000