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 or higher, and Gradle
- An
OPENAI_API_KEYfrom the OpenAI Platform
Launch Phoenix
- Local
- Container
- Self-Host
pip install arize-phoenix && phoenix serve does the same thing. See Terminal setup for customization.Install
Add the dependencies tobuild.gradle:
The
-parameters compiler flag is required. It keeps parameter names in the class file so the annotation agent can record them as span attributes. Without it, parameters appear as arg0, arg1, and so on.Setup
Set the Phoenix endpoint, the project name, and your OpenAI key:OITracer wrapping an OpenTelemetry tracer, and that OITracer registered with the OpenInferenceAgent so the intercepted methods know where to emit spans. The exporter sends spans over OTLP HTTP to <PHOENIX_COLLECTOR_ENDPOINT>/v1/traces.
Available annotations
Parameters are 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 annotated code
Annotate your service methods with the span kind that fits each step. The agent intercepts the methods at class load, so there’s nothing else to wire up.Observe
Open Phoenix and select theannotation-tracing-example project. Each call to service.answer(...) produces one trace:
qa-agent span to inspect the captured inputs and outputs of each step.
Troubleshooting
- No spans, but
OpenInferenceAgentInstaller.install()ran. The ByteBuddy agent only rewrites classes loaded after it installs. Makeinstall()the first statement inmain, and don’t reference any annotated class before that line. - Parameters appear as
arg0,arg1, and so on. The compiler stripped parameter names. AddcompileJava { options.compilerArgs += '-parameters' }tobuild.gradle, as in the Install step. - Spans dropped at JVM exit.
BatchSpanProcessorexports asynchronously. CalltracerProvider.forceFlush().join(...)andtracerProvider.shutdown().join(...)beforemainreturns. - Lost spans across thread boundaries. OpenTelemetry context doesn’t follow work onto a
CompletableFuture,ExecutorService, or reactive pipeline automatically. Wrap the work withio.opentelemetry.context.Context.current().wrap(...). - Hiding sensitive fields. Pass a
TraceConfigwhen you construct theOITracer:new OITracer(provider.get("..."), TraceConfig.builder().hideInputs(true).hideOutputs(true).build()).

