Python SDK

Full observability for Python — databases, HTTP, frameworks, messaging, and logging captured automatically via OpenTelemetry

Built on OpenTelemetry

The Python SDK is a thin wrapper that configures OpenTelemetry with the Obtrace ingest endpoint. It includes every OTel auto-instrumentation for Python with zero extra code.

Installation

pip install obtrace-sdk-python

All OpenTelemetry auto-instrumentations are included by default.

Setup

obtrace_client.py
from obtrace_sdk import ObtraceClient, ObtraceConfig
import os
 
client = ObtraceClient(ObtraceConfig(
    api_key=os.getenv("OBTRACE_API_KEY"),
    service_name="checkout-api",
))

Import this module early in your app. That's it.

What's Captured Automatically

With obtrace-sdk-python, the SDK enables all available OpenTelemetry instrumentations for your installed packages:

WhatHowNeeds code?
All stdlib logginglogging.Handler auto-installed on initNo
Outbound HTTPrequests, httpx, urllib3, aiohttp instrumented via OTelNo
Inbound HTTPdjango, flask, fastapi instrumented via OTel (no middleware needed)No
Databasespsycopg2, asyncpg, sqlalchemy, pymongo instrumented when installedNo
Redisredis-py instrumented via OTelNo
Task queuescelery tasks instrumented with spansNo
gRPCgrpcio client and server calls instrumentedNo
AWSboto3 calls instrumented via OTelNo
Process shutdown flushatexit handler registeredNo

Every logging.info("..."), logging.warning("..."), logging.error("...") in your application (and libraries) is captured and sent to Obtrace as structured logs.

Outbound HTTP, database queries, cache operations, and task queue jobs are all automatically traced with method, URL/query, status, and duration. Trace context (traceparent header) is injected into outbound requests so downstream services can correlate traces.

Opting Out of Auto HTTP Instrumentation

client = ObtraceClient(ObtraceConfig(
    api_key=os.getenv("OBTRACE_API_KEY"),
    service_name="checkout-api",
    auto_instrument_http=False,
))

FastAPI / Flask Middleware

With obtrace-sdk-python, inbound HTTP instrumentation is automatic via OpenTelemetry. No middleware registration is needed for Django, Flask, or FastAPI.

The explicit middleware still works if you prefer manual control:

main.py
from obtrace_sdk.http import fastapi_middleware
 
app = FastAPI()
app.middleware("http")(fastapi_middleware(client))

Logging

Use client.log(level, message, context?) to record events you need to search for later. Good candidates:

  • Caught exceptions and error conditions
  • Business events (order placed, payment failed, user signed up)
  • Audit trail entries (permission changed, API key rotated)
  • State transitions (circuit breaker opened, cache invalidated)
from obtrace_sdk.types import SDKContext
 
client.log("error", "payment.declined", SDKContext(
    attrs={
        "order_id": order_id,
        "provider": "stripe",
        "decline_code": resp.decline_code,
    },
))
 
client.log("info", "user.signup", SDKContext(
    attrs={"plan": "startup"},
))

The level parameter accepts: debug, info, warn, error, fatal. Messages are truncated at 32KB.

The context parameter is optional. When provided, it attaches structured attributes to the log entry. Use attrs for custom key-value pairs, and the built-in fields (trace_id, span_id, method, endpoint, status_code) to correlate logs with traces.

Metrics

Use client.metric(name, value, unit?, context?) to record measurements you want to graph or alert on. Good candidates:

  • Latency measurements (how long did this take?)
  • Counters (how many orders per minute?)
  • Gauges (current queue depth, active connections)
  • Business KPIs (revenue per checkout, items per cart)
client.metric("http.server.duration", 142.5, "ms", SDKContext(
    method="POST",
    endpoint="/api/checkout",
))
 
client.metric("checkout.revenue", 49.99, "USD")
 
client.metric("queue.depth", len(work_queue))

The unit parameter defaults to "1". Follow OTLP conventions: "ms" for milliseconds, "By" for bytes, "1" for dimensionless values (counts, ratios), or a currency code.

Tracing / Spans

Use client.span(...) to track a unit of work and its duration. Returns a dict with trace_id and span_id so you can propagate context to downstream services. Good candidates:

  • HTTP handler execution (request in, response out)
  • Database queries
  • External API calls
  • Background task processing
  • Any operation where you need to see where time was spent
trace = client.span("db.query", attrs={
    "db.system": "postgresql",
    "db.statement": "SELECT * FROM orders WHERE id = %s",
})
 
client.span("http.client POST /payments", trace_id=trace["trace_id"], attrs={
    "http.method": "POST",
    "http.url": "https://api.stripe.com/v1/charges",
})

Omit trace_id and span_id to auto-generate them. Pass an existing trace_id to group spans into the same trace. The status_code uses OTLP conventions: 0 = unset, 1 = OK, 2 = error.

Propagating Trace Context

Trace context is injected automatically on outbound requests and httpx calls. For other HTTP clients, inject manually:

trace = client.span("http.client GET /inventory", attrs={
    "http.method": "GET",
    "http.url": "https://inventory.internal/stock",
})
 
headers = client.inject_propagation(
    trace_id=trace["trace_id"],
    span_id=trace["span_id"],
)
resp = requests.get("https://inventory.internal/stock", headers=headers)

Framework Integration

FastAPI

main.py
import os
 
from fastapi import FastAPI
from obtrace_sdk import ObtraceClient, ObtraceConfig
from obtrace_sdk.http import fastapi_middleware
 
client = ObtraceClient(ObtraceConfig(
    api_key=os.getenv("OBTRACE_API_KEY"),
    service_name="checkout-api",
))
 
app = FastAPI()
app.middleware("http")(fastapi_middleware(client))
 
@app.post("/checkout")
async def checkout():
    client.log("info", "checkout.started")
    return {"ok": True}

The middleware emits one span (http.server METHOD) and one log per request, with method, route, status code, and duration attached.

Flask

app.py
from flask import Flask, request, g
from obtrace_sdk.http import flask_before_after
 
before, after = flask_before_after(client)
 
app = Flask(__name__)
 
@app.before_request
def before_request():
    g.obtrace = before()
 
@app.after_request
def after_request(response):
    after(g.obtrace, request.method, request.path, response.status_code)
    return response

Django

settings.py
MIDDLEWARE = [
    "obtrace_sdk.django.ObtraceMiddleware",
]
 
OBTRACE = {
    "API_KEY": os.getenv("OBTRACE_API_KEY"),
    "SERVICE_NAME": "checkout-api",
}

Configuration Reference

FieldTypeDefaultDescription
api_keystrRequired. Your Obtrace API key.
service_namestrRequired. Stable name for this service.
service_versionstr"0.0.0"Deployment version (git SHA, semver, date).
tenant_idstrNoneScoped ingest identity.
project_idstrNoneScoped ingest identity.
app_idstrNoneScoped ingest identity.
envstrNoneEnvironment name (prod, staging, dev).
auto_instrument_httpboolTruePatch requests and httpx with automatic spans and trace propagation.
request_timeout_secfloat5.0HTTP timeout per OTLP request in seconds.
max_queue_sizeint1000Max queued telemetry items before oldest are dropped.
validate_semantic_metricsboolFalseWhen True + debug, warns on non-canonical metric names.
debugboolFalseEnables SDK diagnostic output to stderr.
default_headersdict{}Extra headers sent with every OTLP request.

Validation Checklist

After integrating the SDK, verify these before shipping to production:

  • service_name, env, and service_version are set and survive restarts and deploys
  • At least one request path emits both a log and a span
  • client.flush() is called before process exit, or you use the context manager
  • No 401 or 403 errors appear during OTLP submission (check with debug=True)
  • Metrics use correct units (ms, By, 1) not free-form strings
  • Outbound HTTP calls show auto-generated spans (or use inject_propagation manually if auto HTTP is disabled)
  • Database queries (psycopg2, sqlalchemy, etc.) show auto-generated spans when using obtrace-sdk-python
  • FastAPI/Flask/Django requests are auto-instrumented via OTel (explicit middleware is optional)

See also: Semantic Metrics

On this page