---
title: Tracer
seo:
  title: Tracer — Python SDK
  description: >-
    Capture execution traces and LLM performance metrics for your application.
    (Python SDK)
description: Capture execution traces and LLM performance metrics for your application.
---

`Tracer` is the primary way to add observability to AI agents and LLM
pipelines. It records spans (units of work), automatically captures
inputs/outputs, and exports everything to the Judgment dashboard.

**Getting started:**

1. Call `Tracer.init()` to create and activate a tracer.
2. Decorate your functions with `@Tracer.observe()` to trace them.
3. Optionally wrap LLM clients with `Tracer.wrap()` for automatic
   token/cost tracking.

Basic setup and usage:

```python
from judgeval import Tracer

tracer = Tracer.init(project_name="search-assistant")

@Tracer.observe(span_type="tool")
def search(query: str) -> str:
    return vector_db.search(query)

@Tracer.observe(span_type="agent")
async def answer(question: str) -> str:
    context = search(question)
    return await llm.generate(question, context)
```

Wrap an LLM client for automatic instrumentation:

```python
from openai import OpenAI

openai = Tracer.wrap(OpenAI())
```

## Attributes

| Prop | Type | Default | Description |
| - | - | - | - |
| `TRACER_NAME?` | `Any` | `JUDGEVAL_TRACER_INSTRUMENTING_MODULE_NAME` | |
| `SUPPORTS_LIVE_INSTRUMENTATION?` | `bool` | `True` | |
| `project_name?` | `Any` | `project_name` | |
| `project_id?` | `Any` | `project_id` | |
| `api_key?` | `Any` | `api_key` | |
| `organization_id?` | `Any` | `organization_id` | |
| `api_url?` | `Any` | `api_url` | |
| `environment?` | `Any` | `environment` | |
| `serializer?` | `Any` | `serializer` | |

***

## init()

Create and activate a new Tracer.

This is the recommended way to initialize tracing. Credentials are
read from environment variables (`JUDGMENT_API_KEY`, `JUDGMENT_ORG_ID`,
`JUDGMENT_API_URL`) when not passed explicitly. If credentials are
missing, the tracer still works but spans won't be exported.

```python
tracer = Tracer.init(
    project_name="search-assistant",
    environment="production",
)
```

```python
def init(project_name=None, api_key=None, organization_id=None, api_url=None, environment=None, set_active=True, serializer=safe_serialize, resource_attributes=None, sampler=None, span_limits=None, span_processors=None) -> Tracer:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `project_name?` | `Optional[str]` | `None` | Your Judgment project name. Required for span export. |
| `api_key?` | `Optional[str]` | `None` | Judgment API key. Defaults to JUDGMENT_API_KEY env var. |
| `organization_id?` | `Optional[str]` | `None` | Organization ID. Defaults to JUDGMENT_ORG_ID env var. |
| `api_url?` | `Optional[str]` | `None` | API endpoint URL. Defaults to JUDGMENT_API_URL env var. |
| `environment?` | `Optional[str]` | `None` | Label for this deployment (e.g. "staging", "production"). Shows up in the Judgment dashboard. |
| `set_active?` | `bool` | `True` | If True (default), sets this as the global tracer so @Tracer.observe() and other static methods use it. |
| `serializer?` | `Callable[[Any], str]` | `safe_serialize` | Custom serializer for span inputs/outputs. |
| `resource_attributes?` | `Optional[Dict[str, Any]]` | `None` | Extra OpenTelemetry resource attributes. |
| `sampler?` | `Optional[Sampler]` | `None` | Custom OpenTelemetry sampler. |
| `span_limits?` | `Optional[SpanLimits]` | `None` | OpenTelemetry span limits. |
| `span_processors?` | `Optional[Sequence[SpanProcessor]]` | `None` | Additional span processors appended after the default Judgment processor. |

### Returns

`Tracer` - A configured and active `Tracer` instance.

***

## set\_active()

Set this tracer as the globally active tracer.

```python
def set_active() -> bool:
```

### Returns

`bool` - True if the tracer was successfully activated.

***

## get\_span\_exporter()

Return the span exporter for this tracer.

Returns a no-op exporter when monitoring is disabled.

```python
def get_span_exporter() -> JudgmentSpanExporter:
```

### Returns

`JudgmentSpanExporter` - The `JudgmentSpanExporter` (or no-op variant) for this tracer.

***

## get\_span\_processor()

Return the span processor for this tracer.

Returns a no-op processor when monitoring is disabled.

```python
def get_span_processor() -> JudgmentSpanProcessor:
```

### Returns

`JudgmentSpanProcessor` - The `JudgmentSpanProcessor` (or no-op variant) for this tracer.

***

## get\_current\_span()

Return the currently active span from the Judgment tracer provider.

```python
def get_current_span() -> opentelemetry.trace.Span:
```

### Returns

`opentelemetry.trace.Span` - The active `Span` object.

***

## force\_flush()

Send all pending spans to Judgment immediately.

Call this before your process exits (e.g. in a serverless function)
to ensure no spans are lost. Does not shut down the tracer.

```python
def lambda_handler(event, context):
    result = process(event)
    Tracer.force_flush()
    return result
```

```python
def force_flush(timeout_millis=30000) -> bool:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `timeout_millis?` | `int` | `30000` | Maximum wait time in milliseconds. |

### Returns

`bool` - True if all spans were flushed within the timeout.

***

## shutdown()

Flush pending spans and shut down the tracer.

Call this on application exit to ensure all data is exported
before the process terminates.

```python
def shutdown(timeout_millis=30000) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `timeout_millis?` | `int` | `30000` | Maximum wait time in milliseconds. |

### Returns

`None`

***

## registerOTELInstrumentation()

Register a third-party OpenTelemetry instrumentor with Judgment.

Use this to route spans from libraries like `opentelemetry-instrumentation-requests`
through the Judgment trace pipeline.

```python
def registerOTELInstrumentation(instrumentor) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `instrumentor` | `Any` | - | |

### Returns

`None`

***

## getOTELTracer()

Get the underlying OpenTelemetry `Tracer` instance.

```python
def getOTELTracer() -> opentelemetry.trace.Tracer:
```

### Returns

`opentelemetry.trace.Tracer` - The OpenTelemetry `Tracer`.

***

## start\_span()

Start a new span that must be ended manually with `span.end()`.

Prefer the `span` context manager for automatic lifecycle management.

```python
def start_span(name, attributes=None) -> opentelemetry.trace.Span:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `name` | `str` | - | Name for the new span. |
| `attributes?` | `Optional[Dict[str, Any]]` | `None` | Optional dictionary of initial span attributes. |

### Returns

`opentelemetry.trace.Span` - The newly started `Span`.

***

## start\_as\_current\_span()

Start a span and set it as the current span in the context.

```python
def start_as_current_span(name, attributes=None) -> Iterator[Span]:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `name` | `str` | - | Name for the new span. |
| `attributes?` | `Optional[Dict[str, Any]]` | `None` | Optional dictionary of initial span attributes. |

### Returns

`Iterator[Span]`

***

## continue\_trace()

Continue a distributed trace from an upstream service.

Extracts W3C trace context and Judgment baggage from `carrier`
and makes it the active context for the duration of the block.
Any span started inside — including `@Tracer.observe` functions
— becomes a child of the upstream parent, stitching your service
into the caller's trace.

Use this at the entry point of an inbound request (HTTP handler,
message queue consumer, RPC dispatcher, etc.) to join the trace
started by the upstream caller.

FastAPI:

```python
@Tracer.observe(span_type="agent")
def handle(payload): ...

@app.post("/run")
async def run(request: Request):
    with Tracer.continue_trace(request.headers):
        return handle(await request.json())
```

Propagating in the opposite direction (outbound):

```python
from judgeval.trace.propagation import inject

headers = {}
inject(headers)
httpx.post(downstream_url, headers=headers, json=payload)
```

```python
def continue_trace(carrier) -> Iterator[Any]:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `carrier` | `Any` | - | A mapping containing propagation keys. Typically request.headers from FastAPI, Flask, or Starlette, but any dict-shaped mapping with lowercase keys works (message queue attributes, Lambda event headers, RPC metadata, etc.). If the carrier contains no trace context, the block runs with a fresh context — no error. |

### Returns

`Iterator[Any]`

***

## scoped\_context()

Temporarily apply Judgment context to spans created in the block.

This is useful before integration-created spans exist, such as around
a wrapped LLM client call. Context is restored when the block exits.

```python
def scoped_context(*, session_id=None, customer_id=None, customer_user_id=None, attributes=None) -> Iterator[None]:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `session_id?` | `Optional[str]` | `None` | |
| `customer_id?` | `Optional[str]` | `None` | |
| `customer_user_id?` | `Optional[str]` | `None` | |
| `attributes?` | `Optional[Dict[str, Any]]` | `None` | |

### Returns

`Iterator[None]`

***

## start\_linked\_trace()

Start a linked trace rooted at a new span.

The new span is the root of a fresh trace. It links back to the
current span via an OpenTelemetry `Link` and stores explicit
cross-trace source/target IDs on the linked root and invocation spans.

```python
def start_linked_trace(name, attributes=None, *, span_type='span') -> Iterator[Span]:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `name` | `str` | - | Name for the linked trace root span. |
| `attributes?` | `Optional[Dict[str, Any]]` | `None` | Optional dictionary of initial linked-root-span attributes. |
| `span_type?` | `Optional[str]` | `'span'` | Span kind to apply to both the parent-side invocation span and the linked trace root span. Set to None to skip setting it. |

### Returns

`Iterator[Span]`

***

## span()

Open a child span using a `with` block.

Use this for tracing a section of code that isn't a standalone
function. Exceptions are automatically recorded on the span.

```python
with Tracer.span("process-results"):
    results = parse(raw_data)
    Tracer.set_attribute("result_count", len(results))
```

```python
def span(span_name) -> Iterator[Span]:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `span_name` | `str` | - | Name for this span (visible in the dashboard). |

### Returns

`Iterator[Span]`

***

## observe()

Decorator that automatically traces a function call.

Wraps any sync or async function in a span. When `fork=True` and an
active parent span exists, eligible calls run in a fresh linked trace
while a parent-side invocation span remains on the current trace.
Generator and async-generator functions stay on the normal observation
path. Inputs and outputs are captured automatically. Works with or
without parentheses.

Basic usage:

```python
@Tracer.observe(span_type="tool")
def search(query: str) -> list[str]:
    return vector_db.search(query)
```

Async functions work the same way:

```python
@Tracer.observe(span_type="agent")
async def answer(question: str) -> str:
    context = search(question)
    return await llm.generate(question, context)
```

Fork a call into a linked trace:

```python
@Tracer.observe(span_type="agent", fork=True)
def delegate(task: str) -> str:
    return run_subsystem(task)
```

Without parentheses (uses default settings):

```python
@Tracer.observe
def my_function():
    ...
```

```python
def observe(func=None, span_type='span', span_name=None, record_input=True, record_output=True, disable_generator_yield_span=False, fork=False) -> C | Callable[[C], C]:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `func?` | `Optional[C]` | `None` | The function to wrap (set implicitly when used as @Tracer.observe without parentheses). |
| `span_type?` | `Optional[str]` | `'span'` | The kind of span. Use "tool", "agent", "llm", or "function" to categorize work in the dashboard. Defaults to "span". |
| `span_name?` | `Optional[str]` | `None` | Override the span name (defaults to the function name). |
| `record_input?` | `bool` | `True` | Capture and store function arguments. Set to False for functions with sensitive or very large inputs. |
| `record_output?` | `bool` | `True` | Capture and store the return value. |
| `disable_generator_yield_span?` | `bool` | `False` | Suppress per-yield child spans for generator functions. |
| `fork?` | `bool` | `False` | If True, run the function in a new linked trace instead of the current trace when an active parent span is available. Otherwise, observation falls back to the normal behavior. |

### Returns

`C | Callable[[C], C]`

***

## wrap()

Wrap an LLM client for automatic tracing of all API calls.

Supported providers: **OpenAI**, **Anthropic**, **Together AI**, and
**Google GenAI**. Once wrapped, every API call made through the client
is recorded as a span with model name, token counts, and cost.

```python
from openai import OpenAI
from anthropic import Anthropic

openai = Tracer.wrap(OpenAI())
anthropic = Tracer.wrap(Anthropic())
```

```python
def wrap(client) -> TClient:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `client` | `TClient` | - | An LLM provider client instance (e.g. OpenAI(), Anthropic()). |

### Returns

`TClient` - The same client instance, now instrumented with tracing.

***

## set\_span\_kind()

Set the `judgment.span_kind` attribute on the current span.

```python
def set_span_kind(kind) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `kind` | `str` | - | |

### Returns

`None`

***

## set\_llm\_span()

```python
def set_llm_span() -> None:
```

### Returns

`None`

***

## set\_tool\_span()

```python
def set_tool_span() -> None:
```

### Returns

`None`

***

## set\_general\_span()

```python
def set_general_span() -> None:
```

### Returns

`None`

***

## set\_attribute()

Attach a custom key-value pair to the current span.

Use this to record application-specific metadata that you want
to see in the Judgment dashboard. Non-primitive values (dicts,
lists, objects) are serialized to strings automatically.

```python
Tracer.set_attribute("user_tier", "premium")
Tracer.set_attribute("search_results_count", len(results))
```

```python
def set_attribute(key, value) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `key` | `str` | - | Attribute name (e.g. "user_tier", "search_results_count"). |
| `value` | `Any` | - | The value to record. |

### Returns

`None`

***

## set\_attributes()

Set multiple custom attributes on the current span at once.

```python
def set_attributes(attributes) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `attributes` | `Dict[str, Any]` | - | Dictionary of key-value pairs to set. |

### Returns

`None`

***

## set\_input()

Manually set the input for the current span.

Use when `@observe(record_input=False)` is set but you want to
record a sanitized or transformed version of the input.

```python
def set_input(input_data) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `input_data` | `Any` | - | The input value to record. |

### Returns

`None`

***

## set\_output()

Manually set the output for the current span.

Use when `@observe(record_output=False)` is set but you want to
record a sanitized or transformed version of the output.

```python
def set_output(output_data) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `output_data` | `Any` | - | The output value to record. |

### Returns

`None`

***

## recordLLMMetadata()

Record model, token usage, and cost on the current span.

If you're using `Tracer.wrap()` this is called automatically. Use
this method when you need to record metadata for a custom LLM
integration.

```python
@Tracer.observe(span_type="llm")
def call_custom_model(prompt: str) -> str:
    response = my_model.generate(prompt)
    Tracer.recordLLMMetadata({
        "model": "my-model-v2",
        "output_tokens": response.usage.output,
        "total_cost_usd": response.usage.cost,
    })
    return response.text
```

```python
def recordLLMMetadata(metadata) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `metadata` | `LLMMetadata` | - | A dict with keys like model, provider, non_cached_input_tokens, output_tokens, and total_cost_usd. All fields are optional. |

### Returns

`None`

***

## set\_customer\_id()

Associate the current trace with a customer.

Once set, this ID propagates to all child spans and enables
per-customer analytics in the Judgment dashboard. Call this
early in your request handler.

```python
@Tracer.observe(span_type="agent")
def handle_request(user_id: str, question: str):
    Tracer.set_customer_id(user_id)
    return answer(question)
```

```python
def set_customer_id(customer_id) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `customer_id` | `str` | - | Your internal customer identifier. |

### Returns

`None`

***

## set\_customer\_user\_id()

Set the customer user ID on the current span and propagate to children.

```python
def set_customer_user_id(customer_user_id) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `customer_user_id` | `str` | - | The customer user ID to associate with this trace. |

### Returns

`None`

***

## set\_session\_id()

Associate the current trace with a conversation session.

Groups multiple requests into a session in the Judgment dashboard.
Propagates to all child spans. Call this early in your request handler.

```python
@Tracer.observe(span_type="agent")
def handle_message(session_id: str, message: str):
    Tracer.set_session_id(session_id)
    return chatbot.respond(message)
```

```python
def set_session_id(session_id) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `session_id` | `str` | - | Your session or conversation identifier. |

### Returns

`None`

***

## set\_propagating\_attribute()

Set an attribute and propagate it to all child spans via baggage.

Unlike `set_attribute` (single span only). The `judgment.`
prefix is reserved and ignored.

```python
def set_propagating_attribute(key, value) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `key` | `str` | - | |
| `value` | `str` | - | |

### Returns

`None`

***

## tag()

Add tags to the current trace for filtering in the dashboard.

Tags are sent asynchronously and appear in the Judgment monitoring
view. Useful for marking traces by feature, experiment, or user segment.

```python
Tracer.tag("rag-pipeline")
Tracer.tag(["experiment-v2", "premium-user"])
```

```python
def tag(tags) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `tags` | `str \| list[str]` | - | A single tag string or a list of tags. |

### Returns

`None`

***

## async\_evaluate()

Run a hosted evaluation on this span when it completes.

The evaluation is queued and processed server-side by the Judgment
platform after the span ends. Use this to score live traffic
without blocking your application.

```python
@Tracer.observe(span_type="agent")
def answer(question: str) -> str:
    response = llm.generate(question)
    Tracer.async_evaluate(
        "faithfulness",
        {"input": question, "actual_output": response},
    )
    return response
```

```python
def async_evaluate(judge, example=None) -> None:
```

### Parameters

| Prop | Type | Default | Description |
| - | - | - | - |
| `judge` | `str` | - | Name of the hosted judge/scorer (e.g. "faithfulness", "answer_relevancy"). |
| `example?` | `Optional[Dict[str, Any]]` | `None` | Optional dict with evaluation data. Keys like input, actual_output, expected_output, and retrieval_context are commonly used. |

### Returns

`None`
