Use this file to discover all available pages before exploring further.
Spring AI is the Java/Spring port of LangChain-style LLM orchestration — model abstraction, tool calling, structured output, and prompt templates that integrate with Spring’s broader ecosystem. Arize AX captures every Spring AI chat-model call via the com.arize:openinference-instrumentation-springAI artifact, registered as a Micrometer observation handler.
Spring AI plugs into the Micrometer ObservationRegistry. The SpringAIInstrumentor is an observation handler that turns each Spring AI call into an OpenInference span. Wire it up in main:
// src/main/java/example/Main.javapackage example;import com.arize.instrumentation.OITracer;import com.arize.instrumentation.TraceConfig;import com.arize.instrumentation.springAI.SpringAIInstrumentor;import io.micrometer.observation.ObservationRegistry;import io.opentelemetry.api.common.AttributeKey;import io.opentelemetry.api.common.Attributes;import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;import io.opentelemetry.context.propagation.ContextPropagators;import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;import io.opentelemetry.sdk.OpenTelemetrySdk;import io.opentelemetry.sdk.resources.Resource;import io.opentelemetry.sdk.trace.SdkTracerProvider;import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;import org.springframework.ai.chat.model.ChatResponse;import org.springframework.ai.chat.prompt.Prompt;import org.springframework.ai.openai.OpenAiChatModel;import org.springframework.ai.openai.OpenAiChatOptions;import org.springframework.ai.openai.api.OpenAiApi;import java.time.Duration;import java.util.Map;import java.util.concurrent.TimeUnit;public class Main { public static void main(String[] args) { String arizeKey = System.getenv("ARIZE_API_KEY"); String spaceId = System.getenv("ARIZE_SPACE_ID"); String project = System.getenv().getOrDefault( "ARIZE_PROJECT_NAME", "spring-ai-tracing-example"); Resource resource = Resource.getDefault().merge(Resource.create( Attributes.of( AttributeKey.stringKey("service.name"), "spring-ai", // Literal value of the SEMRESATTRS_PROJECT_NAME constant // from com.arize:openinference-semantic-conventions. AttributeKey.stringKey("openinference.project.name"), project))); OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder() .setEndpoint("https://otlp.arize.com:443") .setHeaders(() -> Map.of( "authorization", arizeKey, "arize-space-id", spaceId, "arize-interface", "java")) .build(); SdkTracerProvider tracerProvider = SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(exporter) .setScheduleDelay(Duration.ofSeconds(1)) .build()) .setResource(resource) .build(); OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .setPropagators(ContextPropagators.create( W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); System.out.println("Arize AX tracing initialized for Spring AI."); // Wrap the SDK tracer in an OITracer and register the Spring AI // instrumentor as an observation handler. OITracer tracer = new OITracer( tracerProvider.get("com.arize.spring-ai"), TraceConfig.getDefault()); ObservationRegistry registry = ObservationRegistry.create(); registry.observationConfig() .observationHandler(new SpringAIInstrumentor(tracer)); // Build a Spring AI chat model that reports observations to the // registered registry. Reads OPENAI_API_KEY from the environment. OpenAiApi openAiApi = OpenAiApi.builder() .apiKey(System.getenv("OPENAI_API_KEY")) .build(); OpenAiChatOptions options = OpenAiChatOptions.builder() .model("gpt-5") .build(); OpenAiChatModel chatModel = OpenAiChatModel.builder() .openAiApi(openAiApi) .defaultOptions(options) .observationRegistry(registry) .build(); ChatResponse response = chatModel.call(new Prompt( "Why is the ocean salty? Answer in two sentences.")); System.out.println(response.getResult().getOutput().getText()); // Force flush + shutdown — without these, the JVM may exit // before the BatchSpanProcessor delivers its queue. tracerProvider.forceFlush().join(10, TimeUnit.SECONDS); tracerProvider.shutdown().join(10, TimeUnit.SECONDS); }}
Arize AX tracing initialized for Spring AI.The ocean is salty because rivers continuously dissolve mineral salts from rocks and soil and carry them to the sea, where they accumulate over millions of years. Water leaves the ocean through evaporation but the salts remain, steadily concentrating until reaching today's roughly 3.5% salinity.
Open your Arize AX space and select project spring-ai-tracing-example.
You should see a new trace within ~30–60 seconds (Arize’s Java OTLP ingest is slightly slower than the Python path) containing a generate LLM span with the prompt, response, model name (gpt-5), and token-usage attributes attached.
No traces in Arize. Confirm ARIZE_SPACE_ID and ARIZE_API_KEY are set in the same shell that runs gradle run. To confirm spans are being produced locally before troubleshooting export, add SimpleSpanProcessor.create(LoggingSpanExporter.create()) as an extra processor — it prints every span to stderr.
No spans, but the request succeeded. Spring AI only emits observations when the chat model is built with an observationRegistry. Make sure your OpenAiChatModel.builder() chain includes .observationRegistry(registry) and that the registry has the SpringAIInstrumentor registered as an observation handler.
401 from OpenAI. Verify OPENAI_API_KEY is set and has access to gpt-5. Swap model("gpt-5") in OpenAiChatOptions.builder() for a model your key can call.
Spans dropped at JVM exit.BatchSpanProcessor exports asynchronously. Always tracerProvider.forceFlush().join(...) and tracerProvider.shutdown().join(...) before main returns.
Using Spring Boot. This guide targets a plain Spring AI library use-case (no @SpringBootApplication). For a Spring Boot starter pattern with auto-configuration, see Arconia — it bundles Spring AI + OpenInference + OTel auto-config under one dependency.