Implementation:Guardrails ai Guardrails Runner Tracing
| Knowledge Sources | |
|---|---|
| Domains | Telemetry, OpenTelemetry, Observability |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
The Runner Tracing module provides OpenTelemetry instrumentation decorators for tracing the Runner's step and call operations, supporting synchronous, asynchronous, and streaming execution modes.
Description
This module implements two categories of tracing instrumentation for the Guardrails runner:
Step Instrumentation
Step-level tracing wraps individual validation iterations (steps) within a Guard execution. Four decorator variants are provided:
- trace_step -- Wraps a synchronous step function. Creates an OpenTelemetry span named
"step", sets attributes including guardrails version, guard name, step index, serialized inputs, and serialized outputs. On error, sets the span status to ERROR. - trace_async_step -- The async equivalent of
trace_step, for use withasyncstep functions. - trace_stream_step -- Wraps a synchronous generator (streaming step). Yields each
ValidationOutcomeitem while maintaining the tracing span context across the entire generator lifecycle. Captures the final iteration for span attributes in thefinallyblock. - trace_async_stream_step -- The async generator equivalent of
trace_stream_step, yieldingValidationOutcomeitems from an async iterator.
Call Instrumentation
Call-level tracing wraps individual LLM API calls made within a step:
- trace_call -- Wraps a synchronous LLM call function. Creates a span named
"call", serializes inputs and outputs (with redaction of sensitive keys), and handles streaming responses by deferring attribute logging. - trace_async_call -- The async equivalent of
trace_call.
Common Behavior
All decorators check settings.disable_tracing before creating spans. When tracing is disabled, the wrapped function executes directly without instrumentation overhead. When the optional openinference library is available, spans are annotated with SpanAttributes.OPENINFERENCE_SPAN_KIND = "GUARDRAIL".
Input serialization applies redaction to sensitive keys via the recursive_key_operation utility.
Usage
These decorators are applied internally to the Runner's step and call methods. They are not typically used directly by end users but can be leveraged when building custom runner implementations that need OpenTelemetry observability.
Code Reference
Source Location
- Repository: Guardrails
- File:
guardrails/telemetry/runner_tracing.py - Lines: 1-338
Signature
# Step instrumentation
def add_step_attributes(
step_span: Span, response: Optional[Iteration], *args, **kwargs
) -> None: ...
def trace_step(fn: Callable[..., Iteration]) -> Callable[..., Iteration]: ...
def trace_stream_step(
fn: Callable[..., Iterator[ValidationOutcome[OT]]]
) -> Callable[..., Iterator[ValidationOutcome[OT]]]: ...
def trace_async_step(
fn: Callable[..., Awaitable[Iteration]]
) -> Callable[..., Awaitable[Iteration]]: ...
def trace_async_stream_step(
fn: Callable[..., AsyncIterator[ValidationOutcome[OT]]]
) -> Callable[..., AsyncIterator[ValidationOutcome[OT]]]: ...
# Call instrumentation
def add_call_attributes(
call_span: Span, response: Optional[LLMResponse], *args, **kwargs
) -> None: ...
def trace_call(fn: Callable[..., LLMResponse]) -> Callable[..., LLMResponse]: ...
def trace_async_call(
fn: Callable[..., Awaitable[LLMResponse]]
) -> Callable[..., Awaitable[LLMResponse]]: ...
Import
from guardrails.telemetry.runner_tracing import trace_step, trace_call
from guardrails.telemetry.runner_tracing import trace_async_step, trace_async_call
from guardrails.telemetry.runner_tracing import trace_stream_step, trace_async_stream_step
I/O Contract
Inputs
trace_step / trace_async_step
| Name | Type | Required | Description |
|---|---|---|---|
| fn | Callable[..., Iteration] or Callable[..., Awaitable[Iteration]] |
Yes | The step function to instrument. Receives runner arguments including the step index. |
trace_call / trace_async_call
| Name | Type | Required | Description |
|---|---|---|---|
| fn | Callable[..., LLMResponse] or Callable[..., Awaitable[LLMResponse]] |
Yes | The LLM call function to instrument. |
trace_stream_step / trace_async_stream_step
| Name | Type | Required | Description |
|---|---|---|---|
| fn | Callable[..., Iterator[ValidationOutcome]] or Callable[..., AsyncIterator[ValidationOutcome]] |
Yes | The streaming step generator function to instrument. |
Outputs
| Decorator | Return Type | Description |
|---|---|---|
| trace_step | Callable[..., Iteration] |
Wrapped function that creates a "step" span and returns the Iteration result. |
| trace_async_step | Callable[..., Awaitable[Iteration]] |
Async wrapped function with "step" span. |
| trace_stream_step | Callable[..., Iterator[ValidationOutcome]] |
Wrapped generator with span spanning the full iteration lifecycle. |
| trace_async_stream_step | Callable[..., AsyncIterator[ValidationOutcome]] |
Async wrapped generator with span spanning the full iteration lifecycle. |
| trace_call | Callable[..., LLMResponse] |
Wrapped function that creates a "call" span and returns the LLMResponse. |
| trace_async_call | Callable[..., Awaitable[LLMResponse]] |
Async wrapped function with "call" span. |
Span Attributes
The following OpenTelemetry span attributes are set by the tracing functions:
| Attribute | Value | Scope |
|---|---|---|
guardrails.version |
GUARDRAILS_VERSION |
Step and Call |
type |
"guardrails/guard/step" or "guardrails/guard/step/call" |
Step or Call |
guard.name |
Name of the current guard | Step and Call |
step.index |
The iteration index | Step only |
input.mime_type |
"application/json" |
Step and Call |
input.value |
JSON-serialized and redacted input arguments | Step and Call |
output.mime_type |
"application/json" |
Step and Call (when output exists) |
output.value |
JSON-serialized output | Step and Call (when output exists) |
SpanAttributes.OPENINFERENCE_SPAN_KIND |
"GUARDRAIL" |
Step (when openinference is available) |
Usage Examples
from guardrails.telemetry.runner_tracing import trace_step, trace_call
# Decorating a synchronous step function
@trace_step
def my_step(runner, index, *args, **kwargs):
# Perform validation iteration
return iteration_result
# Decorating an LLM call function
@trace_call
def call_llm(prompt, model, **kwargs):
# Make LLM API call
return llm_response