OpenTracing instrumentation for JAX-RS standard. It supports tracing of server and client requests.
Instrumentation by default adds a set of standard HTTP tags and as an operation name it uses a string defined in @Path
annotation.
Custom tags or operation name can be added via span decorators.
This instrumentation also supports tracing of (de)serialization of response and requests bodies.
This implementation is compatible with MicroProfile-OpenTracing (MP-OT). It can be used as a building block of MicroProfile compatible application server. Note that application servers have to add a few things which are not provided by this project: CDI interceptor, automatically register tracing filters into client... SmallRye-OpenTracing uses this library to provide a vendor neutral implementation of MP-OT.
Tracing server requests requires two components: JAX-RS dynamic feature and servlet filter. Span is started in JAX-RS filter and finished in servlet filter.
Tracing can be automatically enabled by adding the following dependency on classpath.
This mechanism requires a tracer to be registered in GlobalTracer
. This is typically done in
ServletContextListener
. Note that JAX-RS clients are not automatically instrumented. Client tracing
feature has to be explicitly registered to all client instances.
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-jaxrs2-discovery</artifactId>
</dependency>
For custom configuration use the following dependency:
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-jaxrs2</artifactId>
</dependency>
The Custom configuration can be achieved by adding ServerTracingDynamicFeature
to Application.singletons
or by wrapping the feature with a class annotated with @Provider
.
This approach does not require adding all classes to singletons set.
Dynamic feature registration via custom provider:
@Provider
public class TracingInitializer implements DynamicFeature {
private final ServerTracingDynamicFeature serverTracingDynamicFeature =
new ServerTracingDynamicFeature.Builder(GlobalTracer.get())
.withOperationNameProvider(ClassNameOperationName.newBuilder())
.build();
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
serverTracingDynamicFeature.configure(resourceInfo, context);
}
}
Dynamic feature registration via singletons:
public class JaxRsApp extends javax.ws.rs.core.Application {
@Override
public Set<Object> getSingletons() {
DynamicFeature tracing = new ServerTracingDynamicFeature.Builder(tracer)
.withDecorators(decorators)
.withSerializationDecorators(serializationDecorators)
.build();
return Collections.singleton(tracing);
}
}
Filter registration:
@WebListener
public class OpenTracingContextInitializer implements javax.servlet.ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
io.opentracing.tracer tracer = new ....
GlobalTracer.register(tracer); // or preferably use CDI
ServletContext servletContext = servletContextEvent.getServletContext();
Dynamic filterRegistration = servletContext
.addFilter("tracingFilter", new SpanFinishingFilter());
filterRegistration.setAsyncSupported(true);
filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC), false, "*");
}
}
An example of traced REST endpoint:
@GET
@Path("/hello")
@Traced(operationName = "helloRenamed") // optional, see javadoc
public Response hello() { // optional to get server span context
// this span will be ChildOf of span representing server request processing
Span childSpan = tracer.buildSpan("businessOperation")
.start())
// business logic
childSpan.finish();
return Response.status(Response.Status.OK).build();
}
Client client = ClientBuilder.newBuilder()
.reqister(ClientTracingFeature.class)
.build();
Response response = client.target("http://localhost/endpoint")
.request()
.property(TracingProperties.CHILD_OF, parentSpanContext) // optional, by default new parent is inferred from span source
.property(TracingProperties.TRACING_DISABLED, false) // optional, by default everything is traced
.get();
Async requests are executed in a different thread than when the client has been invoked, therefore
spans representing client requests are not connected to appropriate parent. To fix this JAX-RS client
has to use OpenTracing-aware ExecutorService
.
@ClientAsyncExecutor
public class DelegateExecutorServiceProvider implements ExecutorServiceProvider {
private final ExecutorService executorService;
public DelegateExecutorServiceProvider(ExecutorService executorService) {
this.executorService = executorService;
}
@Override
public ExecutorService getExecutorService() {
return executorService;
}
@Override
public void dispose(ExecutorService executorService) {
}
}
Client client = ClientBuilder.newBuilder()
.register(new DelegateExecutorServiceProvider(
new TracedExecutorService(Executors.newFixedThreadPool(8), tracer)))
...
Client client = new ResteasyClientBuilder()
.asyncExecutor(new TracedExecutorService(Executors.newFixedThreadPool(8), tracer))
...
./mvnw clean install
Follow instructions in RELEASE