31 / 67 · 06 Observability-Driven Testing · Distributed Tracing with OpenTelemetry← prev⊞ allnext →☰ Read as one page
5.4Instrumenting a Python Service
Automatic Instrumentation Setup
# otel_setup.py -- OpenTelemetry configuration for a Flask service
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
def setup_telemetry(service_name: str, service_version: str):
"""Initialize OpenTelemetry with OTLP export."""
resource = Resource.create({
"service.name": service_name,
"service.version": service_version,
"deployment.environment": "production",
})
# Traces
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4317"))
)
trace.set_tracer_provider(trace_provider)
# Metrics
metric_reader = PeriodicExportingMetricReader(
OTLPMetricExporter(endpoint="http://otel-collector:4317"),
export_interval_millis=10000,
)
metrics.set_meter_provider(MeterProvider(
resource=resource, metric_readers=[metric_reader]
))
# Auto-instrument frameworks
FlaskInstrumentor().instrument()
RequestsInstrumentor().instrument()
SQLAlchemyInstrumentor().instrument()
Manual Spans for Business Logic
# app.py -- Adding manual spans for business-critical operations
from opentelemetry import trace
tracer = trace.get_tracer("order-service")
def process_order(order_data):
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", order_data["id"])
span.set_attribute("order.item_count", len(order_data["items"]))
span.set_attribute("order.total_cents", order_data["total"])
with tracer.start_as_current_span("validate_order"):
validate(order_data)
with tracer.start_as_current_span("charge_payment") as payment_span:
result = charge(order_data["payment"])
payment_span.set_attribute("payment.method", result.method)
payment_span.set_attribute("payment.processor", result.processor)
with tracer.start_as_current_span("reserve_inventory"):
reserve(order_data["items"])
span.set_attribute("order.status", "completed")