OpenTracing Spring Boot Instrumentation

DZone 's Guide to

OpenTracing Spring Boot Instrumentation

OpenTracing is an open standard designed for distributed tracing. Here, we see how to get it integrated with a Spring Boot app and switching between tracing systems.

· Java Zone ·
Free Resource

In this demo series, we are going to look at how simple it is to instrument various Java frameworks using OpenTracing. You will see that it requires minimal changes to the application code. By the time we're done, we will have microservice apps deployed on Kubernetes and all services will be traced with an OpenTracing-compliant tracing system.

In this first demo, we are going to develop and trace a simple Spring Boot app.

Create a Web App

First, let’s write a simple web app. Or better, let’s generate it! All we have to do is just to select a web dependency.

Spring boot generator

Now the application is generated, but it does not contain a web controller. We are going to implement a simple controller with two methods. One will return a greeting and the other creates an HTTP request which calls hello. This demonstrates simple request chaining between services. Do not worry, all the code is on GitHub. At the bottom of this article, you will find all necessary links.

Hello Controller:

public class HelloController {

    private RestTemplate restTemplate;

    public String hello() {
        return "Hello from Spring Boot!";

    public String chaining() {
        ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:8080/hello", String.class);
        return "Chaining + " + response.getBody();

Now the application can serve requests for URLs http://localhost:8080/hello and http://localhost:8080/chaining. The app is still not instrumented, we won’t see any data coming to a tracing system.


Instrumentation with OpenTracing integrations is very simple. For Spring Boot, there is an auto-configuration that instruments all REST controllers and RestTemplate beans. Just add the following dependency to the classpath:


This dependency requires only one thing — a tracer bean, which will be used to report data to the chosen tracing system. If we don’t specify this bean, auto-configuration will choose NoopTracer.

Because we are using OpenTracing instrumentation, we are not bound to any specific tracing system. We will now show how to first use Jaeger and then switch to Zipkin. We will see that changing the tracing system is just a matter of configuration.

As we mentioned, the only tracing configuration needed here is to provide a tracer bean.


Creating a Jaeger tracer is very simple. It just requires a sampler configuration and, because it is a demo, we are going to sample all requests. Note that we are not specifying the URL to a Jaeger server. By default, it will assume that it runs on localhost.

public io.opentracing.Tracer jaegerTracer() {
    return new Configuration("spring-boot", new Configuration.SamplerConfiguration(ProbabilisticSampler.TYPE, 1),
        new Configuration.ReporterConfiguration())

Now we can start the Jaeger server using docker run --rm -it --network=host jaegertracing/all-in-one, then compile and run our app. When everything is up and running, generate some requests to the URLs defined in the previous section.

Open the Jaeger UI on http://localhost:16686:

Jaeger showing reported tracesFigure 2: Jaeger showing reported traces.

In the picture, we can see traces for the request to the /chaining endpoint. There are three spans: one representing server processing of /chaining, the second representing a client request to /hello, and the third being the server processing of the /hello endpoint.


Now let’s benefit from OpenTracing and switch tracing systems with O(1) effort. To do that, we just need to provide an instance of the Zipkin tracer bean. Do not forget to comment out the Jaeger tracer bean. Otherwise, instrumentation would not know which tracer to use.

The Zipkin configuration is very similar — it just needs to know the Zipkin URL:

public io.opentracing.Tracer zipkinTracer() {
    OkHttpSender okHttpSender = OkHttpSender.create("http://localhost:9411/api/v1/spans");
    AsyncReporter<Span> reporter = AsyncReporter.builder(okHttpSender).build();
    Tracing braveTracer = Tracing.newBuilder().localServiceName("spring-boot").reporter(reporter).build();
    return BraveTracer.create(braveTracer);

The Zipkin server can be started with docker run --rm -it -p 9411:9411 openzipkin/zipkin. Now we have to rebuild and start our demo app and generate requests.

Zipkin showing reported tracesFigure 3: Zipkin showing reported traces.

This screenshot also shows traces for the invocation of the /chaining endpoint. In this case, it shows only two spans because Zipkin uses a shared span model, which means that client and server invocation of /hello uses the same span. This is a great example that shows how different OpenTracing providers might model and show things differently.



We have seen how simple it is to instrument Spring Boot with OpenTracing. This instrumentation leverages all key OpenTracing benefits like: vendor-neutrality, O(1) change of tracing systems, or wiring different instrumentations together. In the next blog post, we will look at JAX-RS instrumentation, and in the last demo, all the applications will be deployed on Kubernetes and traced using Jaeger’s production deployment with a Cassandra cluster.

Jaegertracing, java, opentracing, spring boot, tracing, tutorial

Published at DZone with permission of Pavol Loffay , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}