Python setup
Picks up from the Python quickstart. Read that first.
Zero-code vs. programmatic
The quickstart uses zero-code auto-instrumentation via
opentelemetry-instrument. That's the right default. The programmatic
path is needed when:
- You run under a custom process manager that doesn't let you prefix the start command.
- You need to conditionally enable instrumentation (dev vs. prod).
- You want explicit control over the tracer provider.
Programmatic bootstrap:
# otel.py
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
import os
resource = Resource.create({
"service.name": "api",
"service.version": os.getenv("APP_VERSION", "0.0.0"),
"greenslope.release.id": os.getenv("GIT_SHA", "local"),
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(
endpoint="https://ingest.greenslope.io/v1/otel/v1/traces",
headers={"x-greenslope-key": os.environ["GREENSLOPE_INGEST_KEY"]},
)))
trace.set_tracer_provider(provider)Import otel at the top of your entry file, before importing Flask,
FastAPI, or Django.
ASGI vs. WSGI
FastAPI and recent Django are ASGI; Flask and older Django are WSGI. Auto-instrumentation picks the right one based on what's installed, so you don't have to pick manually.
If you run FastAPI under Gunicorn, use
gunicorn -k uvicorn.workers.UvicornWorker — the UvicornWorker is what
the instrumentation hooks into.
Manual spans
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
def process_order(order_id: str):
with tracer.start_as_current_span("checkout.process") as span:
span.set_attribute("order.id", order_id)
try:
result = run(order_id)
except Exception as exc:
span.record_exception(exc)
span.set_status(trace.Status(trace.StatusCode.ERROR))
raise
return resultUse start_as_current_span in sync code and start_as_current_span
inside async with blocks in async code — it works in both.
Django gotcha: instrument the management command
Running manage.py migrate under opentelemetry-instrument will
instrument the migration. Usually that's what you want. If you'd rather
skip instrumentation for one-off commands:
OTEL_TRACES_EXPORTER=none python manage.py migrateLogs and metrics
Python, like the other SDKs, should be configured to send traces
only in V1. If you set OTEL_METRICS_EXPORTER or
OTEL_LOGS_EXPORTER, they'll 404 against our ingest endpoint. Leave
them as none.
Related