Get event-level cost attribution working in 5 minutes.
- Python 3.9+
- OpenTelemetry Collector running (see Collector Configuration)
pip install botanuexport OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_SERVICE_NAME=my-serviceOr in Docker / Kubernetes:
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
- OTEL_SERVICE_NAME=my-servicefrom botanu import enable
enable()Call enable() once at application startup. It reads configuration from environment variables — no hardcoded values needed.
from botanu import botanu_workflow
@botanu_workflow("my-workflow", event_id="evt-001", customer_id="cust-42")
async def do_work():
data = await db.query(...)
result = await llm.complete(data)
return resultAll LLM calls, database queries, and HTTP requests inside the function are automatically tracked with the same run_id tied to the event_id.
Entry service (entry/app.py):
from botanu import enable, botanu_workflow, emit_outcome
enable()
@botanu_workflow(
"my-workflow",
event_id=lambda req: req.event_id,
customer_id=lambda req: req.customer_id,
)
async def handle_request(req):
data = await fetch_data(req)
result = await process(data)
emit_outcome("success")
return resultDownstream service (intermediate/app.py):
from botanu import enable
enable() # propagates run_id from incoming request — no decorator needed| Attribute | Example | Description |
|---|---|---|
botanu.run_id |
019abc12-... |
Unique run identifier (UUIDv7) |
botanu.workflow |
my-workflow |
Workflow name |
botanu.event_id |
evt-001 |
Business event identifier |
botanu.customer_id |
cust-42 |
Customer identifier |
gen_ai.usage.input_tokens |
150 |
LLM input tokens |
gen_ai.usage.output_tokens |
200 |
LLM output tokens |
db.system |
postgresql |
Database system |
All spans across all services share the same run_id, enabling cost-per-event analytics.
- Configuration - Environment variables and YAML config
- Kubernetes Deployment - Zero-code instrumentation at scale
- Context Propagation - How run_id flows across services