apiVersion: v1 kind: ConfigMap metadata: name: my-python-app namespace: meta data: main.py: | import time import random from flask import Flask 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.instrumentation.flask import FlaskInstrumentor from opentelemetry.sdk.resources import Resource import logging from opentelemetry.trace import get_current_span SERVICE_NAME = "my-flask-app" # match your OTEL resource name trace.set_tracer_provider( TracerProvider(resource=Resource.create({"service.name": "my-flask-app"})) ) tracer = trace.get_tracer(__name__) span_processor = BatchSpanProcessor(OTLPSpanExporter()) trace.get_tracer_provider().add_span_processor(span_processor) class TraceIdFilter(logging.Filter): def filter(self, record): span = trace.get_current_span() ctx = span.get_span_context() if ctx.trace_id != 0: record.trace_id = format(ctx.trace_id, "032x") record.span_id = format(ctx.span_id, "016x") record.service_name = SERVICE_NAME else: record.trace_id = None record.span_id = None return True formatter = logging.Formatter( '{"message": "%(message)s", "trace_id": "%(trace_id)s", "span_id": "%(span_id)s", "service_name": "my-flask-app"}' ) handler = logging.StreamHandler() handler.setFormatter(formatter) handler.addFilter(TraceIdFilter()) logger = logging.getLogger("myapp") logger.setLevel(logging.INFO) logger.addHandler(handler) resource = Resource( attributes={ "service.name": "python-otel-demo-app", # 👈 choose any name you like } ) trace.set_tracer_provider(TracerProvider(resource=resource)) tracer = trace.get_tracer(__name__) otlp_exporter = OTLPSpanExporter( endpoint="http://otel-collector.meta.svc:4318/v1/traces", # insecure=True, ) span_processor = BatchSpanProcessor(otlp_exporter) trace.get_tracer_provider().add_span_processor(span_processor) app = Flask(__name__) FlaskInstrumentor().instrument_app(app) def get_trace_id(): span = get_current_span() if span: return span.get_span_context().trace_id return None @app.route("/") def root(): with tracer.start_as_current_span("handle_homepage"): logger.info("handling request", extra={"trace_id": get_trace_id()}) time.sleep(random.random() * 0.05) # simulate request parsing fetch_user() calculate_recommendations() return "Hello from / with traces!" 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: meta 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: meta spec: selector: app: python-otel ports: - port: 80 targetPort: 5000