Spring Boot’s auto-instrumentation for OpenTelemetry tracing doesn’t actually add tracing to your application; it enables existing, latent tracing capabilities by injecting a Java Agent.

Let’s see this in action. Imagine a simple Spring Boot application with a REST controller.

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @RestController
    static class MyController {
        @GetMapping("/hello")
        public String hello() {
            return "Hello!";
        }
    }
}

Without the OpenTelemetry Java Agent, a request to /hello generates no trace data. Now, let’s attach the agent. First, download the latest opentelemetry-javaagent.jar from the OpenTelemetry Java Instrumentation releases page.

Then, launch your Spring Boot application with the -javaagent JVM argument:

java -javaagent:path/to/opentelemetry-javaagent.jar -jar target/demo-0.0.1-SNAPSHOT.jar

Now, when you hit http://localhost:8080/hello, the Java Agent intercepts the incoming HTTP request, creates a trace, and records spans for the web framework’s handling of the request, the controller method execution, and any downstream calls (if they were also instrumented). This data is then exported to a configured backend like Jaeger or OTLP.

The core problem OpenTelemetry auto-instrumentation solves is the boilerplate of manually instrumenting every single library and framework your application uses. Instead of adding @Trace annotations or tracer.span("my_operation").start() calls everywhere, the Java Agent uses bytecode manipulation (specifically, at load time) to weave in tracing logic for common libraries like Spring MVC, Tomcat, JDBC, and more. It effectively turns on a dormant tracing capability that’s already present in these libraries, but requires an external signal to activate.

The magic happens in the opentelemetry-javaagent.jar. This is a self-contained JAR that acts as a JVM Tool Interface (JVMTI) agent. When you start your JVM with -javaagent:path/to/opentelemetry-javaagent.jar, the agent’s premain method is executed before your application’s main method. This allows it to hook into class loading and modify the bytecode of classes as they are loaded. It uses a sophisticated system of "instrumentation modules" that are tailored to specific libraries and frameworks. For Spring MVC, there’s an instrumentation module that targets classes like DispatcherServlet and HandlerAdapter, injecting code to start and stop spans around request processing.

The exact levers you control are primarily through environment variables or system properties that the agent reads.

OTEL_EXPORTER_OTLP_ENDPOINT: This is crucial for telling the agent where to send your trace data. By default, it often points to http://localhost:4318/v1/traces for OTLP/HTTP. If you’re using Jaeger directly, you might set OTEL_EXPORTER_JAEGER_ENDPOINT=http://localhost:14250.

OTEL_SERVICE_NAME: This assigns a logical name to your application in your tracing backend. Without it, all your traces will appear under a generic name, making analysis difficult. Set it like OTEL_SERVICE_NAME=my-spring-app.

OTEL_TRACES_SAMPLER: Controls how many traces are captured. always_on (default for auto-instrumentation) captures everything. traceidratio with a value like 0.1 captures 10% of traces. parentbased_always_on means a trace is sampled if its parent was sampled.

OTEL_INSTRUMENTATION_SPRING_WEBFLUX_ENABLED and OTEL_INSTRUMENTATION_SPRING_WEB_ENABLED: These are boolean flags (true/false) to specifically enable or disable tracing for Spring WebFlux or traditional Spring Web MVC respectively. If you have a mixed environment or want to be explicit, you can toggle these.

The agent also supports a rich set of configuration for other aspects like resource attributes (OTEL_RESOURCE_ATTRIBUTES), metric export, logging export, and more, all via environment variables or system properties.

The most surprising thing about this auto-instrumentation is that it can often trace through your application code without you touching it, creating a surprisingly complete picture of request flow across your services and even into underlying infrastructure like databases or message queues, all by simply attaching a JAR.

The next concept to explore is how to correlate these traces with logs, often achieved through the OpenTelemetry Log SDK and exporter.

Want structured learning?

Take the full Spring-boot course →