Skip to content

OpenTelemetry

If your service is already instrumented with the OpenTelemetry SDK, add AxonPushSpanExporter to your tracer provider and every span you create ships to AxonPush as an app.span event alongside whatever other OTel-compatible backends you already export to.

[!TIP] Non-blocking by default (v0.0.5+)

The exporter pushes span publishes onto a bounded in-memory queue and drains them from a single background daemon thread — span.end() (and the resulting export call) stays O(microseconds) on the caller’s thread. provider.force_flush() drains the internal queue and is safe to call before a process exit. Pass mode="sync" for blocking publishes. See the stdlib logging page for the Lambda / GCF / Azure Functions pattern — the flush_after_invocation decorator works with the span exporter too.

Terminal window
pip install "axonpush[otel]"
import os
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from axonpush import AxonPush
from axonpush.integrations.otel import AxonPushSpanExporter
client = AxonPush(
api_key=os.environ["AXONPUSH_API_KEY"],
tenant_id=os.environ["AXONPUSH_TENANT_ID"],
)
provider = TracerProvider()
provider.add_span_processor(
BatchSpanProcessor(
AxonPushSpanExporter(
client=client,
channel_id=1,
service_name="my-api",
environment="production",
)
)
)
trace.set_tracer_provider(provider)

[!TIP] Multiple exporters

TracerProvider supports multiple span processors — add AxonPushSpanExporter alongside your existing OTLP exporter and spans flow to both backends:

provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(...)))
provider.add_span_processor(BatchSpanProcessor(AxonPushSpanExporter(...)))

Use tracer.start_as_current_span(...) as normal — spans are exported when they close:

tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("POST /chat") as req:
req.set_attribute("http.method", "POST")
req.set_attribute("http.route", "/chat")
with tracer.start_as_current_span("llm.call") as llm:
llm.set_attribute("model", "gpt-4o-mini")
llm.set_attribute("prompt_tokens", 128)
response = call_llm(...)
llm.set_attribute("completion_tokens", 256)
with tracer.start_as_current_span("db.query") as db:
db.set_attribute("db.statement", "SELECT * FROM users WHERE id=$1")
db.set_attribute("db.rows_affected", 1)

Use with the auto-instrumentation packages

Section titled “Use with the auto-instrumentation packages”

OTel ships auto-instrumentation for popular libraries (FastAPI, Django, SQLAlchemy, redis, boto3, …). Install the ones you need and they hook into the tracer provider you just configured — no code changes required:

Terminal window
pip install opentelemetry-instrumentation-fastapi
pip install opentelemetry-instrumentation-sqlalchemy
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
FastAPIInstrumentor().instrument_app(app)
SQLAlchemyInstrumentor().instrument(engine=engine)

Every FastAPI request and SQLAlchemy query now ships to AxonPush.

FieldValue
identifierThe span name
event_typeapp.span
payload.nameSpan name
payload.kindSpan kind (INTERNAL, SERVER, CLIENT, …)
payload.attributesAll span attributes (stringified)
payload.statusSpan status (OK, ERROR, UNSET)
payload.startTimeUnixNano / payload.endTimeUnixNanoSpan timing
payload.resourceservice.name, service.version, deployment.environment
trace_idThe OTel trace ID, preserved end-to-end
span_idThe OTel span ID