Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

OpenTracing JAX-RS Instrumentation

DZone's Guide to

OpenTracing JAX-RS Instrumentation

Implementing RESTful Web Services into your Java application is really just a matter of adding a dependency and registering a tracer instance. Sounds easy enough, right?

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

In the previous demo, we 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 the generate button.

Wildfy Swarm app generator.

Figure 1: Wildfly Swarm app generator.

The generated application contains one REST endpoint which returns a 'hello world' string. This endpoint is accessible on http://localhost:8080/hello. In the next step, we are going to 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 the 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();
  }
}

Note that we are using 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 the 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 the business layer. If we created span in anotherAction then its parent would be the span created in the action method.

Jaeger showing reported spans.

Figure 2: 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 the tracer producer code. No changes to the application or business logic! In the next demo, we will wire this app with the Spring Boot created in the previous demo and deploy them on Kubernetes.

Resources

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
opentracing ,tracing ,jaegertracing

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 }}