For practical setup, see Auto-instrumentation, Manual instrumentation, and Combining auto and manual. This page covers what each approach is doing under the hood.
Auto-instrumentation
Auto-instrumentation automatically collects telemetry from supported libraries — no per-call code changes needed. Recommended whenever an OpenInference auto-instrumentor exists for your stack.Initializing an Auto-instrumentor
How Auto-instrumentors Work
Auto-instrumentors use a technique called monkey patching to wrap library functions with tracing logic. The mechanics: When you callOpenAIInstrumentor().instrument():
- Identify — the instrumentor targets specific functions in the library (e.g.,
openai.chat.completions.create). - Wrap — the original function is replaced with a wrapper that:
- Creates a span before the original function runs.
- Calls the original function.
- Sets span attributes from the function’s arguments and response (e.g.,
llm.input_messages,llm.output_messages,llm.token_count.*). - Ends the span.
instrument() call time, depending on the library). After that, every call to the wrapped function flows through the tracing logic transparently.
For the source of every OpenInference instrumentor, see the OpenInference repository.
Manual Instrumentation
Manual instrumentation means creating each span explicitly using the OTeltracer and setting attributes following the OpenInference semantic conventions.
The most common pattern uses start_as_current_span so the new span automatically becomes a child of the most recent active span in the OTel context:
start_as_current_span does two important things:
- It automatically sets the span as the active span in the OTel context, so any spans created inside the block become children.
- It automatically calls
span.end()when the context-manager block exits, so you don’t have to manage span lifetime by hand.
parent_id — which is exactly what you want for nested operations.
Pros and Cons
Common Pitfalls
A few failure modes worth knowing about:- Forgetting to end a span — spans are only handed off to the span processor when
end()is called. A never-ended span never exports. Usestart_as_current_span(which auto-ends) to avoid this entirely. - Not setting span status — set
OkorErrorexplicitly so the Arize AX UI can flag failures. - Missing important semantic conventions:
openinference.span.kind— without this, the Arize AX UI can’t pick the right icon.input.valueandoutput.value— without these, the trace list view shows blanks.llm.input_messagesandllm.output_messageson LLM spans — the chat history doesn’t render in the Arize AX UI without them.tool.nameandtool.parameterson tool spans — tool detail panels stay empty.
Hybrid Instrumentation
Auto-instrumentors are an easy way to begin tracing, but they only know about what the underlying library exposes. The most interesting parts of an AI application — tool execution in user code, custom business logic, agent loops, evaluation steps — often need extra spans or extra attributes the instrumentor can’t see. Hybrid instrumentation combines auto-instrumentation with targeted manual work. Three primary ways to extend an auto-instrumented trace:Custom Span Processor
Custom span processors are the most powerful hybrid pattern. You write a class that implementson_start and on_end (and optionally the upcoming on_ending) and attach it to the Tracer Provider.
What you can do at each hook:
The classic use case is PII redaction: walk every outgoing span and scrub sensitive values from the attributes. For practical examples, see Mask and redact data.
For the full lifecycle details and how
on_end immutability varies between Python and TypeScript, see the Span Processor page.
Choosing an Approach
A practical decision tree:- Is there an OpenInference auto-instrumentor for your stack? Use it. Start there.
- Are there gaps it doesn’t cover (tool execution, business logic, custom retrieval)? Add manual spans for those operations.
- Do you need to attach the same context to many spans (session ID, user ID)? Use the context managers.
- Do you need centralized policy across every span (PII redaction, attribute enrichment, conditional filtering)? Write a custom span processor.