Skip to main content

Documentation Index

Fetch the complete documentation index at: https://arize-ax.mintlify.dev/docs/llms.txt

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.

Prerequisites

  • Java 17+ (Spring AI 1.0+ requires 17)
  • An Arize AX account (sign up)
  • An OPENAI_API_KEY from the OpenAI Platform

Launch Arize

  1. Sign in to your Arize AX account.
  2. From Space Settings, copy your Space ID and API Key. You will set them as ARIZE_SPACE_ID and ARIZE_API_KEY below.

Install

Add the dependencies to build.gradle:
plugins {
    id 'application'
    id 'io.spring.dependency-management' version '1.1.7'
}

repositories {
    mavenCentral()
}

ext {
    set('springAiVersion', "1.0.0")
}

dependencies {
    // OpenInference Spring AI instrumentor + semantic conventions
    implementation 'com.arize:openinference-instrumentation-springAI:0.1.9'
    implementation 'com.arize:openinference-semantic-conventions:0.1.12'

    // Spring AI OpenAI provider (BOM pulls in the rest)
    implementation 'org.springframework.ai:spring-ai-starter-model-openai'
    implementation 'io.micrometer:micrometer-tracing-bridge-brave:1.5.1'

    // OpenTelemetry SDK + OTLP exporter
    implementation 'io.opentelemetry:opentelemetry-sdk:1.50.0'
    implementation 'io.opentelemetry:opentelemetry-exporter-otlp:1.50.0'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.ai:spring-ai-bom:${springAiVersion}"
    }
}

application {
    mainClass = 'example.Main'
}
Note the artifact name uses camelCaseopeninference-instrumentation-springAI, not spring-ai. That’s how it’s published on Maven Central.

Configure credentials

export ARIZE_SPACE_ID="<your-space-id>"
export ARIZE_API_KEY="<your-api-key>"
export ARIZE_PROJECT_NAME="spring-ai-tracing-example"
export OPENAI_API_KEY="<your-openai-api-key>"

Setup tracing

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.java
package 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);
    }
}

Run Spring AI

gradle run

Expected output

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.

Verify in Arize

  1. Open your Arize AX space and select project spring-ai-tracing-example.
  2. 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.
  3. If no traces appear, see Troubleshooting.

Troubleshooting

  • 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.

Resources

Spring AI Documentation

OpenInference Spring AI Instrumentor (Maven Central)

OpenInference Spring AI Source

Spring AI Example (with tools + multi-turn)