---
title: "OpenTelemetry instrumentation libraries"
description: "Send spans from FastAPI, SQLAlchemy, Celery and other OpenTelemetry instrumentation packages to Judgment."
sidebar:
  label: "OTel instrumentation libraries"
seo:
  title: "OpenTelemetry instrumentation libraries | Judgment Docs"
  description: "Send spans from FastAPI, SQLAlchemy, Celery and other OpenTelemetry instrumentation packages to Judgment."
---

Judgment didn't write these packages. They all connect the same way, so this one page covers them.

## Give the instrumentor Judgment's provider

`Tracer.init` doesn't install a global OpenTelemetry provider. An instrumentor that isn't handed a provider uses the global one, which by default is a no-op, so its spans go nowhere. A plain `FastAPIInstrumentor.instrument_app(app)` after `Tracer.init` exports nothing.

Give the instrumentor Judgment's provider in one of three ways, in this order of preference:

1. Register it with Judgment. `Tracer.registerOTELInstrumentation(X())` calls `X().instrument(tracer_provider=<Judgment's provider>)` for you.
2. Pass the provider yourself when the library's API instruments one object at a time: `tracer_provider=JudgmentTracerProvider.get_instance()`.
3. Install Judgment globally with `JudgmentTracerProvider.install_as_global_tracer_provider()` when you can't reach the call. Only the first global install wins; see [Frameworks with built-in OpenTelemetry](/documentation/connect/integrations/frameworks-with-built-in-otel).

**Python**

```python title="app.py"
from fastapi import FastAPI
from judgeval import JudgmentTracerProvider, Tracer
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor

tracer = Tracer.init(project_name="lunch-order-agent")
Tracer.registerOTELInstrumentation(HTTPXClientInstrumentor())

app = FastAPI()
FastAPIInstrumentor.instrument_app(
    app, tracer_provider=JudgmentTracerProvider.get_instance()
)

@app.middleware("http")
async def activate_tracer(request, call_next):
    tracer.set_active()
    return await call_next(request)
```

**TypeScript**

```typescript title="instrumentation.ts"
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { JudgmentTracerProvider, Tracer } from "judgeval";

JudgmentTracerProvider.getInstance();
Tracer.registerOTELInstrumentation(new HttpInstrumentation());

export const tracerReady = Tracer.init({ projectName: "lunch-order-agent" });
```

Register instrumentations in a CommonJS file your app loads before anything else. Instrumentations patch modules when they're loaded, so a module your app imported earlier stays unpatched. `JudgmentTracerProvider.getInstance()` creates Judgment's provider so the instrumentation can register before `Tracer.init()` finishes; without it, the SDK logs `No active tracer provider. Instrumentation was not registered.` See [Set up tracing](/documentation/connect/set-up-tracing#use-auto-instrumentation).

### Web servers: activate the tracer per request

Judgment's provider sends each span to the tracer that's active in the current execution context. A web server may run requests in contexts that never saw `Tracer.init`. The judgeval repository's own FastAPI example handles this with the middleware shown above: keep the object `Tracer.init` returns and call `tracer.set_active()` at the start of each request. In Flask, do the same in a `@app.before_request` function.

## Library table

Install `judgeval`, the library, and the package in the second column. "Register" means `Tracer.registerOTELInstrumentation(Class())`.

| Library | Package | Class | How to wire it |
|---|---|---|---|
| FastAPI | `opentelemetry-instrumentation-fastapi` | `FastAPIInstrumentor` | Register before creating `FastAPI()`, or `FastAPIInstrumentor.instrument_app(app, tracer_provider=...)` |
| Starlette | `opentelemetry-instrumentation-starlette` | `StarletteInstrumentor` | `StarletteInstrumentor.instrument_app(app, tracer_provider=...)` |
| Flask | `opentelemetry-instrumentation-flask` | `FlaskInstrumentor` | Register before creating `Flask(__name__)`, or `FlaskInstrumentor().instrument_app(app, tracer_provider=...)` |
| Django | `opentelemetry-instrumentation-django` | `DjangoInstrumentor` | Register in `manage.py` and in `wsgi.py`/`asgi.py`, before Django loads settings |
| aiohttp server | `opentelemetry-instrumentation-aiohttp-server` | `AioHttpServerInstrumentor` | Register |
| Any ASGI app | `opentelemetry-instrumentation-asgi` | `OpenTelemetryMiddleware` | `app = OpenTelemetryMiddleware(app, tracer_provider=...)` |
| Any WSGI app | `opentelemetry-instrumentation-wsgi` | `OpenTelemetryMiddleware` | `app = OpenTelemetryMiddleware(app, tracer_provider=...)` |
| httpx | `opentelemetry-instrumentation-httpx` | `HTTPXClientInstrumentor` | Register |
| requests | `opentelemetry-instrumentation-requests` | `RequestsInstrumentor` | Register |
| aiohttp client | `opentelemetry-instrumentation-aiohttp-client` | `AioHttpClientInstrumentor` | Register |
| SQLAlchemy | `opentelemetry-instrumentation-sqlalchemy` | `SQLAlchemyInstrumentor` | Register before `create_engine`, or `SQLAlchemyInstrumentor().instrument(engine=engine, tracer_provider=...)` |
| psycopg 3 | `opentelemetry-instrumentation-psycopg` | `PsycopgInstrumentor` | Register |
| asyncpg | `opentelemetry-instrumentation-asyncpg` | `AsyncPGInstrumentor` | Register |
| PyMongo | `opentelemetry-instrumentation-pymongo` | `PymongoInstrumentor` | Register |
| MySQL Connector | `opentelemetry-instrumentation-mysql` | `MySQLInstrumentor` | Register |
| sqlite3 | `opentelemetry-instrumentation-sqlite3` | `SQLite3Instrumentor` | Register |
| Redis | `opentelemetry-instrumentation-redis` | `RedisInstrumentor` | Register |
| Celery | `opentelemetry-instrumentation-celery` | `CeleryInstrumentor` | Call `Tracer.init` and register inside a `worker_process_init` signal handler, so each worker process has its own tracer |
| AWS Lambda | `opentelemetry-instrumentation-aws-lambda` | `AwsLambdaInstrumentor` | Register at module level and call `Tracer.force_flush()` before the handler returns. See [Tracing patterns](/documentation/connect/tracing-patterns) |

Every `tracer_provider=...` above is `JudgmentTracerProvider.get_instance()`, imported with `from judgeval import JudgmentTracerProvider`. Module paths follow the package name: `opentelemetry-instrumentation-httpx` is `from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor`.

Celery, as a full example:

```python title="worker.py"
from celery import Celery
from celery.signals import worker_process_init
from judgeval import Tracer
from opentelemetry.instrumentation.celery import CeleryInstrumentor

app = Celery("lunch_orders", broker="redis://localhost:6379/0")

@worker_process_init.connect(weak=False)
def init_tracing(*args, **kwargs):
    Tracer.init(project_name="lunch-order-agent")
    Tracer.registerOTELInstrumentation(CeleryInstrumentor())
```

## LLM instrumentation families

Three projects publish instrumentors for model providers and agent frameworks.

| Family | Packages | How it reaches Judgment | Mapping in Judgment |
|---|---|---|---|
| OpenInference (Arize) | `openinference-instrumentation-<name>`, module `openinference.instrumentation.<name>` | Register | Built-in OpenInference mapper: span kind, input, output, model, tokens |
| OpenLLMetry (Traceloop) | `opentelemetry-instrumentation-<name>` (Python), `@traceloop/instrumentation-<name>` (TypeScript) | Register | Built-in GenAI mapper: model, provider, tokens. Prompts written as indexed keys may not fill input and output |
| OpenLIT | `openlit` | `Openlit.initialize()` from judgeval | GenAI attributes, as above |

OpenLIT has a judgeval helper that passes Judgment's tracer to `openlit.init` and turns off OpenLIT metrics:

```python
from judgeval import Tracer
from judgeval.integrations.openlit import Openlit

Tracer.init(project_name="lunch-order-agent")
Openlit.initialize()
```

Import it from `judgeval.integrations.openlit`; `from judgeval.integrations import Openlit` raises `ImportError`. Extra keyword arguments go straight to `openlit.init()`.

If a span arrives with the right model and tokens but empty input or output, write a mapper for the library's keys. See [Attribute keys and mappers](/documentation/reference/attribute-keys-and-mappers).
