Skip to main content

Global Instrumentation

Use instrument() to patch SDK classes at startup. This automatically tracks all client instances.
from aden import instrument, MeterOptions, create_console_emitter

# Call ONCE at application startup, BEFORE creating clients
instrument(MeterOptions(
    emit_metric=create_console_emitter(pretty=True),
))

# All clients are now automatically instrumented
from openai import OpenAI
client1 = OpenAI()
client2 = OpenAI(api_key="different-key")

MeterOptions

from dataclasses import dataclass
from typing import Callable, Any

@dataclass
class MeterOptions:
    # Required: where to send metrics
    emit_metric: MetricEmitter

    # Control server connection
    api_key: str | None = None
    server_url: str | None = None
    fail_open: bool = True  # Allow requests if server unreachable

    # Context tracking
    get_context_id: Callable[[], str | None] | None = None

    # Emission settings
    async_emit: bool = False  # Fire-and-forget emission
    sample_rate: float = 1.0  # 1.0 = emit all, 0.5 = 50%

    # Feature flags
    track_tool_calls: bool = True

    # Custom ID generators
    generate_trace_id: Callable[[], str] | None = None
    generate_span_id: Callable[[], str] | None = None

    # Custom metadata added to all events
    request_metadata: dict[str, Any] | None = None

    # Error handling
    on_emit_error: Callable[[Exception], None] | None = None
    on_alert: Callable[[AlertInfo], None] | None = None

    # Request control
    before_request: BeforeRequestHook | None = None

Async Instrumentation

For async applications, use instrument_async():
import asyncio
from aden import instrument_async, uninstrument_async, MeterOptions

async def main():
    result = await instrument_async(MeterOptions(
        emit_metric=my_emitter,
        api_key=os.environ.get("ADEN_API_KEY"),
    ))
    print(f"Instrumented: {result}")
    # InstrumentationResultWithAgent(openai=True, anthropic=False, gemini=False, control_agent=<agent>)

    # ... your async code ...

    await uninstrument_async()

asyncio.run(main())

InstrumentationResult

from aden import instrument, get_instrumented_sdks

result = instrument(options)
print(result)
# InstrumentationResult(openai=True, anthropic=False, gemini=False)

# Or check status later
status = get_instrumented_sdks()
print(status.openai)     # True
print(status.anthropic)  # False
print(status.gemini)     # False

Checking Status

from aden import (
    is_instrumented,
    get_instrumented_sdks,
    is_openai_instrumented,
    is_anthropic_instrumented,
    is_gemini_instrumented,
)

# Check if any SDK is instrumented
if is_instrumented():
    print("Active:", get_instrumented_sdks())

# Check individual providers
print({
    "openai": is_openai_instrumented(),
    "anthropic": is_anthropic_instrumented(),
    "gemini": is_gemini_instrumented(),
})

Removing Instrumentation

from aden import uninstrument

# Remove all instrumentation
uninstrument()

# Or remove specific providers
from aden import uninstrument_openai, uninstrument_anthropic, uninstrument_gemini

uninstrument_openai()
uninstrument_anthropic()
uninstrument_gemini()

Provider-Specific Instrumentation

Instrument providers individually:
from aden import instrument_openai, instrument_anthropic, instrument_gemini

# OpenAI only
instrument_openai(MeterOptions(
    emit_metric=openai_emitter,
))

# Anthropic with different config
instrument_anthropic(MeterOptions(
    emit_metric=anthropic_emitter,
    sample_rate=0.5,  # Sample 50% of requests
))

Runtime Updates

Update options at runtime:
from aden import update_instrumentation_options, get_instrumentation_options

# Check current options
current = get_instrumentation_options()
print(f"Current sample rate: {current.sample_rate}")

# Update options
update_instrumentation_options(
    sample_rate=0.5,
    request_metadata={"version": "2.0"},
)

Control Agent Access

Access the control agent for advanced operations:
from aden import get_control_agent

agent = get_control_agent()
if agent:
    # Get current budget status
    status = await agent.get_budget_status("user_123")
    print(f"Remaining: ${status.remaining_usd}")

Framework Integration

FastAPI

from fastapi import FastAPI, Request
from aden import instrument, MeterOptions, create_console_emitter

app = FastAPI()

@app.on_event("startup")
async def startup():
    instrument(MeterOptions(
        emit_metric=create_console_emitter(pretty=True),
    ))

@app.on_event("shutdown")
async def shutdown():
    from aden import uninstrument
    uninstrument()

Django

# settings.py or apps.py
from aden import instrument, MeterOptions, create_console_emitter

def ready(self):
    instrument(MeterOptions(
        emit_metric=create_console_emitter(pretty=True),
    ))

Flask

from flask import Flask
from aden import instrument, uninstrument, MeterOptions, create_console_emitter

app = Flask(__name__)

with app.app_context():
    instrument(MeterOptions(
        emit_metric=create_console_emitter(pretty=True),
    ))

import atexit
atexit.register(uninstrument)

Best Practices

Do:
  • Call instrument() once at application startup
  • Use instrument_async() for async applications with control server
  • Clean up with uninstrument() on shutdown
  • Use get_context_id for per-user tracking
Don’t:
  • Call instrument() multiple times
  • Create SDK clients before calling instrument()
  • Mix sync and async instrumentation functions

Next Steps