openinference-instrumentation-annotation adds annotation-driven tracing to any Java application. Annotate your methods with @Chain, @LLM, @Tool, @Agent, or @Span and a ByteBuddy agent intercepts each call at class load to produce OpenInference spans backed by OpenTelemetry. Use it when you’ve built an agent or pipeline by hand and want OpenInference semantics without a framework-specific instrumentor.
Prerequisites
- Java 17+ (
-parameterscompiler flag enabled, so the agent can read parameter names — see Install) - An Arize AX account (sign up)
- An
OPENAI_API_KEYfrom the OpenAI Platform
Launch Arize
- Sign in to your Arize AX account.
- From Space Settings, copy your Space ID and API Key. You will set them as
ARIZE_SPACE_IDandARIZE_API_KEYbelow.
Install
Add the dependencies tobuild.gradle:
The annotation artifact uses standard hyphenated naming —
openinference-instrumentation-annotation — unlike openinference-instrumentation-springAI which retains camelCase on Maven Central.Configure credentials
Setup tracing
The annotation instrumentor needs three things at startup: the ByteBuddy agent installed before any annotated class is loaded, anOITracer wrapping an OpenTelemetry tracer, and that OITracer registered with the OpenInferenceAgent so the intercepted methods know where to emit spans.
Available annotations
Parameters are automatically captured as
input.value and the return value as output.value. Use @ExcludeFromSpan on a parameter to drop it from the input attribute, and @SpanMapping to map a parameter or field to a specific OpenInference semantic-convention attribute.
Run Annotations
Annotate your service methods with the span kind that fits each step, then run it. The agent intercepts at class load — there’s nothing further to wire up beyond the annotations themselves.service.answer("Why is the ocean salty? Answer in two sentences.") produces a nested trace:
Expected output
Verify in Arize
- Open your Arize AX space and select project
annotation-tracing-example. - You should see a new trace within ~30–60 seconds (Arize’s Java OTLP ingest is slightly slower than the Python path) with the four-span hierarchy shown above. Click the root
qa-agentspan to inspect captured inputs and outputs. - If no traces appear, see Troubleshooting.
Check from the skill, CLI, or SDK
Confirm spans are actually reaching your Arize AX project. Use whichever fits your workflow — the skill and CLI work for any framework; the SDK check is shown for each language.- Arize skill (agent)
- AX CLI
- SDK
Install the Arize Skills plugin and let your coding agent check for you:Then prompt your agent:
Use the arize-trace skill to export and analyze recent traces from my project. Confirm spans are arriving, and summarize any errors or latency issues.
Troubleshooting
- No spans, but
OpenInferenceAgentInstaller.install()ran. The ByteBuddy agent only rewrites classes loaded after it installs. Make sureinstall()is the first statement inmain, and that you don’t reference any annotated class (directly or transitively via imports) before that line. The example above defersQAServiceloading until afterinstall()by only namingQAServiceinsidemain. - Parameters appear as
arg0,arg1, … in span attributes. The compiler stripped parameter names. AddcompileJava { options.compilerArgs += '-parameters' }to yourbuild.gradle(already in the Install snippet), or fall back to@SpanMapping(parameter = "arg0", ...)to reference the generated names. - No traces in Arize. Confirm
ARIZE_SPACE_IDandARIZE_API_KEYare set in the same shell that runsgradle run. To confirm spans are being produced locally before troubleshooting export, addSimpleSpanProcessor.create(LoggingSpanExporter.create())as an extra processor — it prints every span to stderr. 401from OpenAI. VerifyOPENAI_API_KEYis set and has access togpt-5.5. Swap"gpt-5.5"in theopenAiChatCompletionbody for a model your key can call.- Spans dropped at JVM exit.
BatchSpanProcessorexports asynchronously. AlwaystracerProvider.forceFlush().join(...)andtracerProvider.shutdown().join(...)beforemainreturns. - Lost spans across thread boundaries. The ByteBuddy agent wraps each annotated method on the calling thread. OpenTelemetry context does not automatically follow execution across
CompletableFuture,ExecutorService, reactive frameworks (Reactor, RxJava, Mutiny), or coroutines. Propagate context explicitly withio.opentelemetry.context.Context.current().wrap(...)when handing work to another thread, or fall back to the programmatic span API where you control span lifetimes directly. - Hiding sensitive fields. Pass a
TraceConfigwhen constructing theOITracerto suppress inputs / outputs / tool parameters:new OITracer(provider.get("..."), TraceConfig.builder().hideInputs(true).hideOutputs(true).build()).