Spring AI Observability & Monitoring
Instrument Spring AI with Micrometer and OpenTelemetry: token and cost metrics per feature, latency tracking, tracing model calls, and dashboards that catch a cost problem before the invoice does.
On this page
You cannot operate what you cannot see, and AI features fail in ways ordinary metrics miss — a correct-looking answer that cost ten times too many tokens, a slow retrieval hidden inside an acceptable total latency. This tutorial instruments Spring AI so cost, latency and quality are visible before they become incidents.
Key Takeaways
- Record token usage as a metric tagged by feature from day one — it is your only early warning for a cost problem.
- Trace the full request: retrieval, generation and tools as separate spans.
- Log metadata, not content — prompts contain personal data.
- Turn tokens into a cost metric and alert on the daily rate.
Enable Spring AI observability
Spring AI integrates with Micrometer. Add the Actuator and a registry, and it emits observations for model calls automatically:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>management:
endpoints:
web:
exposure:
include: health, prometheus, metrics
metrics:
tags:
application: ai-service
spring:
ai:
chat:
observations:
# Off by default, and rightly so — prompts may contain personal data.
log-prompt: falseToken metrics per feature — the one that matters most
Aggregate token counts tell you nothing actionable. Tokens tagged by feature tell you which feature is expensive:
public class TokenMetricsAdvisor implements CallAdvisor {
private final MeterRegistry registry;
private final String feature;
public TokenMetricsAdvisor(MeterRegistry registry, String feature) {
this.registry = registry;
this.feature = feature;
}
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
var sample = io.micrometer.core.instrument.Timer.start(registry);
ChatClientResponse response = chain.nextCall(request);
sample.stop(registry.timer("ai.latency", "feature", feature));
var chatResponse = response.chatResponse();
if (chatResponse != null && chatResponse.getMetadata().getUsage() != null) {
var usage = chatResponse.getMetadata().getUsage();
registry.counter("ai.tokens", "feature", feature, "type", "prompt")
.increment(usage.getPromptTokens());
registry.counter("ai.tokens", "feature", feature, "type", "completion")
.increment(usage.getCompletionTokens());
}
return response;
}
@Override public String getName() { return "token-metrics"; }
@Override public int getOrder() { return 100; }
}Attach it per feature so the tags mean something:
ChatClient supportClient = builder
.defaultAdvisors(new TokenMetricsAdvisor(registry, "support"))
.build();Turning tokens into cost
Tokens are the input; money is what people care about. Convert:
@Component
public class CostCalculator {
// Prices per 1M tokens. Keep these in configuration and update them when
// the provider changes pricing.
private record Pricing(double promptPerM, double completionPerM) {}
private final Map<String, Pricing> pricing = Map.of(
"gpt-4o-mini", new Pricing(0.15, 0.60),
"gpt-4o", new Pricing(2.50, 10.00));
public double costUsd(String model, long promptTokens, long completionTokens) {
Pricing p = pricing.getOrDefault(model, new Pricing(0, 0));
return (promptTokens / 1_000_000.0) * p.promptPerM()
+ (completionTokens / 1_000_000.0) * p.completionPerM();
}
}Record it as a metric and you can alert on the daily rate.
Tracing the full request
A total latency of 3.5 seconds is not actionable. A trace showing retrieval took 40ms and generation took 3.4s tells you exactly where to look.
@Observed(name = "rag.request")
public RagAnswer answer(String question) {
List<Document> context = observation("rag.retrieve",
() -> retriever.retrieve(question)); // span 1
return observation("rag.generate",
() -> generate(question, context)); // span 2
}With OpenTelemetry exporting to a backend, you see the waterfall and can attribute latency correctly. Add the OTLP exporter:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>The dashboard that earns its keep
A minimal but sufficient dashboard tracks, per feature:
- Cost per hour (with an alert threshold) — catches runaway loops and context bloat.
- P95 latency, split into retrieval and generation — tells you what to optimise.
- Requests per minute — capacity planning and anomaly detection.
- Error rate by type — provider errors vs your errors.
- Tokens per request trend — a rising trend often means context creeping upward.
Logging: metadata by default, content never (casually)
// Good: safe to log, genuinely useful.
log.info("model call feature={} model={} promptTokens={} latencyMs={}",
feature, model, usage.getPromptTokens(), latency);
// Dangerous: the prompt may contain personal data, and logs go to a much
// wider audience than the model.
// log.info("prompt: {}", fullPrompt); // don'tNext
- Security in AI-powered Spring applications
- AI observability & LLM tracing — tools like LangFuse in depth
- Testing AI applications
Frequently Asked Questions
How do I track token usage in Spring AI?
How do I monitor LLM costs before the bill arrives?
Should I log prompt and response content?
What should I trace in an AI application?
Related tutorials
- Spring AI with Ollama (Local LLMs)Run local LLMs in Spring Boot with Spring AI and Ollama: setup, model selection, offline development, cost and privacy trade-offs, and when a local model is the right call.
- Security in AI-Powered Spring ApplicationsSecure a Spring Boot AI application against the OWASP LLM Top 10: prompt injection defenses, output validation, rate limiting, PII handling and safe tool authorization — with code.
- Multimodal AI with Spring BootSend images and audio to vision models from Spring Boot with Spring AI: the Media API, image analysis, document extraction from scans, and handling multimodal input safely.
- Testing AI ApplicationsHow to test non-deterministic AI code in Spring Boot: mocking the ChatModel for unit tests, golden datasets for retrieval, property-based assertions, and LLM-as-judge for quality.