OpenTracing JAX-RS Instrumentation

A blog post by Pavol Loffay

apm | jaeger | jax-rs | opentracing | tracing



In the previous demo we have demonstrated how to instrument a Spring Boot app using OpenTracing, a vendor-neutral standard for distributed tracing. In this article we are going to instrument a Java API for RESTful Web Services (JAX-RS), and show you how to trace the business layer and add custom data to the trace.

Demo application

Creating a JAX-RS app from scratch can be a time consuming task, therefore in this case we are going to use Wildfly Swarm’s app generator. Select JAX-RS and CDI dependencies and hit generate button.

wf swarm generator
Figure 1: Wildfly Swarm generator.

The generated application contains one REST endpoint which returns hello world string. This endpoint is accessible on http://localhost:8080/hello. In the next step we are going to add instrumentation and simple business logic.

Instrumentation

Adding OpenTracing instrumentation to JAX-RS is very simple, just include the following dependency in the classpath and the tracing feature will be automatically registered.

<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-jaxrs2</artifactId>
</dependency>

OpenTracing is just an API, therefore it is required to register a specific tracer instance. In this demo we are going to use Jaeger tracing system. The tracer should be created and initialized only once per process, hence ServletContextListener is the ideal place for this task:

@WebListener
public class TracingContextListener implements ServletContextListener {

  @Inject
  private io.opentracing.Tracer tracer;

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    GlobalTracer.register(tracer);
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {}

  @Produces
  @Singleton
  public static io.opentracing.Tracer jaegerTracer() {
    return new Configuration("wildfly-swarm", new Configuration.SamplerConfiguration(
        ProbabilisticSampler.TYPE, 1),
        new Configuration.ReporterConfiguration())
        .getTracer();
  }
}

Tracer initialization code requires to specify app name, which is in this case wildfly-swarm and sampler configuration.

Note that we are suing Java’s Context and Dependency Injection (CDI) to share a tracer instance in our app. If we forget to register a specific tracer instance, then the tracing feature would use NoopTracer. Now we can verify tracing by starting Jaeger server using the following command: docker run --rm -it --network=host jaegertracing/all-in-one and accessing the endpoint at http://localhost:8080/hello. Our trace with one span should be present in the UI at http://localhost:16686.

Instrumenting business logic

JAX-RS instrumentation provides nice visibility into your app, however, it is often necessary to add custom data to the trace to see what is happening in the service or database layer.

The following code snippet shows how the service layer can create and add data to the trace:

public class BackendService {

  @Inject
  private io.opentracing.Tracer tracer;

  public String action() throws InterruptedException {
    int random = new Random().nextInt(200);

    try (ActiveSpan span = tracer.buildSpan("action").startActive()) {
      anotherAction();
      Thread.sleep(random);
    }

    return String.valueOf(random);
  }

  private void anotherAction() {
    tracer.activeSpan().setTag("anotherAction", "data");
  }

Note that it’s not necessary to manually pass a span instance around. The method anotherAction accesses the current active span from the tracer.

With the additional instrumentation shown above, an invocation of the REST endpoint would result in a trace consisting of two spans, one representing the inbound server request, and the other the business logic. The span representing server processing is automatically considered as the parent for span created in business layer. If we created span in anotherAction then its parent would be span created in action method.

swarm jaeger
Figure 1: Jaeger showing reported spans.

Video

Conclusion

We have demonstrated that instrumenting a JAX-RS app is just a matter of adding a dependency and registering a tracer instance. If we would like to use a different OpenTracing implementation, Zipkin for instance, it would just require changing tracer producer code. No changes to the application or business logic! In the next demo we will wire this app with Spring Boot created in previous demo and deploy them on Kubernetes.




Published by Pavol Loffay on 10 July 2017

redhatlogo-white

© 2016 | Hawkular is released under Apache License v2.0