How-to · OpenTelemetry

How to Instrument Java and Spring Boot with OpenTelemetry

Instrumenting Java and Spring Boot with OpenTelemetry means attaching the OpenTelemetry Java agent, or using the Spring Boot starter, to emit traces, metrics and logs over OTLP without changing application code..

Instrumenting Java and Spring Boot with OpenTelemetry means attaching the OpenTelemetry Java agent, or using the Spring Boot starter, to emit traces, metrics and logs over OTLP without changing application code.

What to observe in a Java / Spring Boot service

A Java service adds a layer no interpreted runtime has: the JVM. So beyond per-endpoint latency and error rate and JDBC query timing, you watch JVM health, heap usage by pool, garbage-collection pause time and frequency, and thread counts, because the most confusing Java latency, requests that pause for no application reason, is almost always a garbage-collection pause or heap pressure, not the code.

The combination that pinpoints most incidents is an endpoint latency histogram alongside GC pause time and heap-after-GC on the same timeline: when latency spikes line up with GC pauses, you are looking at a memory problem, not a logic one.

How OpenTelemetry collects it

Java has the most mature zero-code path in OpenTelemetry: the Java agent attaches with -javaagent at JVM start and auto-instruments a large set of libraries, the Servlet stack and Spring MVC, JDBC, popular HTTP and messaging clients, so requests and downstream calls are traced without touching the code. It also emits JVM runtime metrics, heap by pool, GC, threads, class loading, from the same agent.

For Spring Boot specifically there is a second option: the OpenTelemetry Spring Boot starter, which integrates with the framework and with Micrometer, the metrics facade Spring Boot Actuator already uses. Both export over OTLP. The practical caution is to pick one path for a given signal so you do not emit JVM metrics twice from both the agent and Micrometer; agent for traces plus one consistent source for metrics is the clean setup.

Metric names follow OpenTelemetry and Micrometer conventions and can vary by version; confirm against your agent or starter build.

Key metrics to watch

The signals that explain most JVM service incidents:

SignalSourceWhat it tells you / watch for
http.server.request.durationServlet/Spring instrumentationPer-endpoint latency histogram; track p95/p99 by route.
jvm.memory.used (by pool)JVM runtimeOld-generation heap that stays high after GC is the signature of a leak.
jvm.gc.durationJVM runtimeLong or frequent pauses freeze request threads and appear as latency spikes.
jvm.thread.countJVM runtimeClimbing thread count can mean a thread pool is not releasing, heading toward exhaustion.
db.client.operation.durationJDBC instrumentationQuery latency; if it dominates a request the fix is in the query or connection pool.
connection pool active/idlepool instrumentationA pool at its max with requests waiting is a common, silent source of latency.

What to put on a dashboard

A Spring Boot dashboard should make a GC problem instantly distinguishable from a code or dependency problem. Row one: endpoint p95/p99 and 5xx by route. Row two, the JVM: heap used by pool (watch old-gen after GC), GC pause time, and thread count. Row three: JDBC query latency and connection-pool utilization, since pool exhaustion is a frequent cause of latency that looks like a slow database.

# p99 latency by endpoint
histogram_quantile(0.99, http.server.request.duration) by (http.route)

# GC pause time (spikes here line up with latency spikes)
rate(jvm.gc.duration[5m])

# old-gen heap after GC: a rising floor = leak
jvm.memory.used{pool="G1 Old Gen"}

Reading the signals: common failure modes

Long GC pauses

Requests pause in lockstep with garbage-collection events while the application code is idle. Overlay jvm.gc.duration on the latency histogram to confirm. Causes are an undersized heap, high allocation rate, or a leak filling old-gen; tune heap and allocation, or fix the leak.

Heap leak

Old-generation heap-after-GC creeps up over hours or days and never returns to its baseline, ending in an OutOfMemoryError. The trend on jvm.memory.used for the old-gen pool is the tell. Common causes are static collections and cache entries that are never evicted.

Thread or connection pool exhaustion

Latency climbs and requests time out while CPU and heap look fine, because threads or JDBC connections are all checked out and new requests wait. Watch thread count and pool active-vs-max; the fix is sizing the pool or finding the leak that holds connections open.

Example alert conditions

Starting thresholds to tune:

ConditionSignalMeaning
5xx rate by route > 2% for 5merrorsEndpoint failing.
jvm.gc.duration rate > 10% of wall timeGCGC is eating a tenth of your runtime; latency will suffer.
old-gen heap after GC trending up over 24hmemoryProbable leak; schedule investigation before OOM.
connection pool active = max for 5mdatabasePool exhausted; requests are waiting on connections.

From monitoring to autonomous resolution

Ops Singularity's TelemetryOps ingests Java and Spring Boot OpenTelemetry data natively, and Sentinel AI resolves the incidents it reveals, including JVM-level issues, through governed Action Tickets. Explore TelemetryOps.

Frequently asked questions

How do I add OpenTelemetry to a Spring Boot app?

Attach the OpenTelemetry Java agent with -javaagent and set the OTLP endpoint, or add the OpenTelemetry Spring Boot starter for framework-native integration with Micrometer metrics.

Does the OpenTelemetry Java agent need code changes?

No. The Java agent attaches at JVM startup and auto-instruments a wide range of libraries, so you get traces and metrics without changing application code.

See autonomous operations on your own stack.

Bring a real incident. We will show you Sentinel investigate, act and verify end to end, with every action reversible and audited.

Request a Demo → See TelemetryOps