Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Arize-ai/openinference/llms.txt
Use this file to discover all available pages before exploring further.
OpenInference provides instrumentation for Spring AI applications using Spring’s Micrometer Observation API. This enables you to trace LLM calls, model parameters, token usage, and more using OpenTelemetry.
Installation
Gradle
Add the following to your build.gradle:
dependencies {
implementation 'com.arize:openinference-instrumentation-springAI:0.1.0'
implementation 'org.springframework.ai:spring-ai-model:1.0.1'
implementation 'io.micrometer:micrometer-observation:1.15.1'
}
Maven
Add the following to your pom.xml:
<dependencies>
<dependency>
<groupId>com.arize</groupId>
<artifactId>openinference-instrumentation-springAI</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-model</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<version>1.15.1</version>
</dependency>
</dependencies>
Requirements
- Java 17 or higher
- Spring AI 1.0.0 or higher
- OpenTelemetry Java 1.49.0 or higher
- Micrometer Observation 1.15.0 or higher
Quick Start
Basic Setup
import com.arize.instrumentation.OITracer;
import com.arize.instrumentation.TraceConfig;
import com.arize.instrumentation.springAI.SpringAIInstrumentor;
import io.micrometer.observation.ObservationRegistry;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
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;
public class MySpringAIApp {
public static void main(String[] args) {
// Initialize OpenTelemetry
SdkTracerProvider tracerProvider = initializeOpenTelemetry();
// Create OITracer
OITracer tracer = new OITracer(
tracerProvider.get("com.mycompany.my-app"),
TraceConfig.getDefault()
);
// Register SpringAI instrumentation with Micrometer
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig()
.observationHandler(new SpringAIInstrumentor(tracer));
// Configure OpenAI chat model
OpenAiApi openAiApi = OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("gpt-4")
.temperature(0.7)
.maxTokens(200)
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(openAiApi)
.defaultOptions(options)
.observationRegistry(registry) // Register the observation registry
.build();
// Use the model - traces are automatically captured
ChatResponse response = chatModel.call(
new Prompt("What is the capital of France?")
);
System.out.println(response.getResult().getOutput());
}
}
OpenTelemetry Setup
Basic Setup with Phoenix
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributeKey;
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 java.time.Duration;
public static SdkTracerProvider initializeOpenTelemetry() {
// Create resource with service information
Resource resource = Resource.getDefault()
.merge(Resource.create(Attributes.of(
AttributeKey.stringKey("service.name"), "spring-ai-app",
AttributeKey.stringKey("service.version"), "1.0.0"
)));
// Create OTLP exporter for Phoenix
OtlpGrpcSpanExporter otlpExporter = OtlpGrpcSpanExporter.builder()
.setEndpoint("http://localhost:4317")
.setTimeout(Duration.ofSeconds(10))
.build();
// Create and configure tracer provider
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(otlpExporter)
.setScheduleDelay(Duration.ofSeconds(1))
.build())
.setResource(resource)
.build();
// Register global OpenTelemetry instance
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.buildAndRegisterGlobal();
return tracerProvider;
}
Configuration
Custom Trace Configuration
Control what information is captured in traces:
import com.arize.instrumentation.TraceConfig;
// Configure what to hide in traces
TraceConfig config = TraceConfig.builder()
.hideInputMessages(false) // Set to true to hide input messages
.hideOutputMessages(false) // Set to true to hide output messages
.build();
OITracer tracer = new OITracer(
tracerProvider.get("my-app"),
config
);
Chat Options
Configure model parameters:
import org.springframework.ai.openai.OpenAiChatOptions;
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("gpt-4")
.temperature(0.7)
.maxTokens(500)
.topP(0.9)
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(openAiApi)
.defaultOptions(options)
.observationRegistry(registry)
.build();
Spring Boot Integration
Application Configuration
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.GlobalOpenTelemetry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ObservationRegistry observationRegistry() {
// Create OITracer
OITracer tracer = new OITracer(
GlobalOpenTelemetry.getTracer("spring-ai-app"),
TraceConfig.getDefault()
);
// Create and configure registry
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig()
.observationHandler(new SpringAIInstrumentor(tracer));
return registry;
}
}
Service Example
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Service;
@Service
public class ChatService {
private final ChatModel chatModel;
public ChatService(ChatModel chatModel) {
this.chatModel = chatModel;
}
public String chat(String message) {
ChatResponse response = chatModel.call(new Prompt(message));
return response.getResult().getOutput().getText();
}
}
Spring AI supports function calling with automatic tracing:
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import java.util.function.Function;
public class WeatherExample {
public record WeatherRequest(String location, String unit) {}
public record WeatherResponse(double temperature, String unit) {}
static class WeatherService implements Function<WeatherRequest, WeatherResponse> {
public WeatherResponse apply(WeatherRequest request) {
// Simulate weather API call
return new WeatherResponse(72.5, "F");
}
}
public static void main(String[] args) {
// Setup OpenTelemetry and registry (as shown above)
SdkTracerProvider tracerProvider = initializeOpenTelemetry();
ObservationRegistry registry = createObservationRegistry(tracerProvider);
// Create tool callback
ToolCallback weatherTool = FunctionToolCallback.builder(
"currentWeather",
new WeatherService()
)
.description("Get the current weather in a location")
.inputType(WeatherRequest.class)
.build();
// Configure model with tools
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("gpt-4")
.temperature(0.4)
.toolCallbacks(weatherTool)
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(openAiApi)
.defaultOptions(options)
.observationRegistry(registry)
.build();
// Ask a question that requires tool calling
ChatResponse response = chatModel.call(
new Prompt("What's the weather like in San Francisco?")
);
System.out.println(response.getResult().getOutput());
}
}
Captured Trace Data
The instrumentation automatically captures:
- LLM Model Information: Model name, provider
- Input Messages: User prompts, system messages, conversation history
- Output Messages: Model responses, assistant messages
- Invocation Parameters: Temperature, max tokens, top_p, etc.
- Token Usage: Prompt tokens, completion tokens, total tokens
- Tool Calls: Function names, arguments, and responses
- Message Roles: System, user, assistant, tool
- Timing Information: Request latency and duration
- Error Information: Exceptions and error messages
Multi-turn Conversations
Trace complete conversations with context:
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
// First turn
Prompt prompt1 = new Prompt("What is the capital of France?");
ChatResponse response1 = chatModel.call(prompt1);
// Second turn with context
Prompt prompt2 = Prompt.builder()
.messages(
new UserMessage("What is the capital of France?"),
response1.getResult().getOutput(),
new UserMessage("What about Germany?")
)
.build();
ChatResponse response2 = chatModel.call(prompt2);
Viewing Traces
Using Phoenix
-
Start Phoenix locally:
docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest
-
Run your instrumented application
-
View traces at http://localhost:6006
Using Other Backends
OpenInference instrumentation works with any OpenTelemetry-compatible backend:
- Jaeger: Change the OTLP endpoint to your Jaeger instance
- Zipkin: Use the Zipkin exporter
- Cloud Providers: AWS X-Ray, Google Cloud Trace, Azure Monitor
Best Practices
- Singleton Registry: Create a single
ObservationRegistry instance and reuse it across your application
- Spring Boot Integration: Use Spring’s dependency injection for
ObservationRegistry
- Set Service Name: Always set a meaningful
service.name in your OpenTelemetry resource
- Use Batch Processing: Use
BatchSpanProcessor for better performance
- Handle Secrets: Never log API keys in traces
- Graceful Shutdown: Flush spans before application shutdown
Troubleshooting
No traces appearing
- Verify the
ObservationRegistry is properly configured with SpringAIInstrumentor
- Ensure the registry is passed to your
ChatModel via .observationRegistry()
- Check that your OTLP endpoint is accessible
- Enable debug logging for Spring AI observations
Missing token counts
Token usage is only available when the model provider returns usage metadata in the response.
Ensure you’re using Spring AI 1.0.0 or higher, which includes observation support for tool calls.
Complete Example
Here’s a complete example with tool calling and conversation context:
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.Attributes;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import org.springframework.ai.chat.messages.UserMessage;
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;
public class CompleteExample {
public static void main(String[] args) {
// 1. Initialize OpenTelemetry
Resource resource = Resource.getDefault().merge(
Resource.create(Attributes.builder()
.put("service.name", "spring-ai-example")
.build())
);
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.setResource(resource)
.build();
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.buildAndRegisterGlobal();
// 2. Create OITracer and register with Micrometer
OITracer tracer = new OITracer(
tracerProvider.get("spring-ai"),
TraceConfig.getDefault()
);
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig()
.observationHandler(new SpringAIInstrumentor(tracer));
// 3. Configure and create chat model
OpenAiApi api = OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("gpt-4")
.temperature(0.7)
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(api)
.defaultOptions(options)
.observationRegistry(registry)
.build();
// 4. Use the model
ChatResponse response = chatModel.call(
new Prompt("Explain quantum computing in one sentence.")
);
System.out.println(response.getResult().getOutput());
// 5. Shutdown and flush traces
tracerProvider.forceFlush();
tracerProvider.shutdown();
}
}
Resources