How to use Opentracing
When MicroProfile-Opentracing has been set up for the application runtime it’s fairly easy to use in the application.
Creating the Tracer
Dependending on the integration of the MicroProfile-Config in an application runtime the Tracer is either set up automatically or can be set up manually via a TracerFactory implementation which is registered via CDI as the following snippet illustrates. The Jaeger tracing backend is either configured via System Properties or environment variables. There is no other way to configure the backend.
public class TracerFactory implements
io.opentracing.contrib.tracerresolver.TracerFactory {
@Override
public Tracer getTracer() {
Configuration configuration = Configuration.fromEnv();
Tracer tracer = configuration.getTracer();
GlobalTracer.register(tracer);
return tracer;
}
}
META-INF/services/io.opentracing.contrib.tracerresolver.TracerFactory
at.ihet.samples.microprofile.opentracing.TracerFactory
The TracerFactory is resolved via SPI by the TracerResolver which is provided by the opentracing-contrib-tracerresolver implementation. This approach ensures that the GlobalTracer knows the actual Tracer.
Enhance Information on Spans
There are default implementations which provide data for the trace spans which will not satisfy most of the users’ needs. Opentracing-contrib-jaxrs2 provides the interface io.opentracing.contrib.jaxrs2.server.ServerSpanDecorator which allows developers to decorate the request and response and add additional information to the span as the following code snippet illustrates:
// Server side span decorator
public class ServerTracingDecorator implements ServerSpanDecorator {
@Override
public void decorateRequest(ContainerRequestContext
requestContext, Span span) {
span.setOperationName(requestContext.getUriInfo().getPath());
}
@Override
public void decorateResponse(ContainerResponseContext
responseContext, Span span) {
span.setBaggageItem("response.body",
JsonbBuilder.create().toJson(response.getEntity()));
}
}
// Register server side tracing via JAX-RS DynamicFeature
@Provider
public class TracingInitializerFeature implements DynamicFeature {
private static DynamicFeature tracingFeature = new
ServerTracingDynamicFeature.Builder(GlobalTracer.get())
// Here we register our decorator
.withDecorators(Collections.singletonList(
new ServerTracingDecorator()))
.withSerializationDecorators(Collections.emptyList())
.withTraceSerialization(false)
.withJoinExistingActiveSpan(false)
.build();
@Override
public void configure(ResourceInfo resourceInfo,
FeatureContext context) {
// Here we register the custom built feature
tracingFeature.configure(resourceInfo, context);
}
}
// Client side span decorator
public class ClientTracingDecorator implements ClientSpanDecorator {
@Override
public void decorateRequest(ClientRequestContext requestContext,
Span span) {
span.setBaggageItem("request.body",
JsonbBuilder.create().toJson(response.getEntity()));
span.setOperationName(String.format("[%s] %s",
requestContext.getUri().getHost(),
requestContext.getUri().getPath()));
}
@Override
public void decorateResponse(ClientResponseContext responseContext,
Span span) {
String body = "";
if (responseContext.hasEntity()) {
body = readEntityFromInputStream(
responseContext.getEntityStream());
responseContext.setEntityStream(new
BufferedInputStream(
new ByteArrayInputStream(
body.getBytes(StandardCharsets.UTF_8))));
}
span.setBaggageItem("response.body", body);
}
private String readEntityFromInputStream(final InputStream is) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
IOUtils.copy(is, bos);
return new String(bos.toByteArray(),StandardCharsets.UTF_8);
} catch (Throwable e) {
return "not_readable: " + e.getMessage();
}
}
}
The client side span decorators won’t work with declarativ registration of the ClientTracingFeature with MicroProfile-Rest-Client annotations because the feature will have to be built manually to be able to use the decorators.
// Register client side tracing via JAX-RS DynamicFeature
private ExternalRestResource createRestClient() {
try {
final ClientTracingFeature feature = createClientTracingFeature();
return RestClientBuilder.newBuilder()
.baseUrl(new URL("http://httpbin.org"))
.readTimeout(2000, TimeUnit.SECONDS)
.connectTimeout(2000, TimeUnit.SECONDS)
// Here we register the custom built feature
.register(feature)
.build(ExternalRestResource.class);
} catch (MalformedURLException e) {
throw new RuntimeException("URI is not valid");
}
}
private ClientTracingFeature createClientTracingFeature() {
return new ClientTracingFeature.Builder(GlobalTracer.get())
.withTraceSerialization(false)
// Here we register our decorator
.withDecorators(Collections.singletonList(
new ClientTracingDecorator()))
.build();
}
Trace CDI Beans
MicroProfile-Opentracing specifies the annotation org.eclipse.microprofile.opentracing.Traced where implementations provide an Interceptor to trace method calls on CDI Beans. The only thing that has to be done by the developer is to annotate CDI Beans on class or method level with @Traced and, if intended, to provide a custom implementation of the Interceptor.
@Traced
public class CustomRestResource {
...
}
Trace rest clients
Unfortunately MicroProfile-Rest-Client doesn’t integrate with MicroProfile-Opentracing yet, but it’s fairly easy to integrate the opentracing-contrib-jaxrs2 provided ClientTracingFeature into rest clients.
// If span decorators are not needed or you have a custom tracing feature
@RegisterRestClient(configKey = "externalResource")
@RegisterProviders(@RegisterProvider(ClientTracingFeature.class))
@Path("/")
public interface ExternalRestResource {
...
}
// If you want to use span decorators
See code snippet Register client side tracing via JAX-RS DynamicFeature
The tracing information is passed between services via an Http Header and the called service can extract the current active span and add its spans as child to it.