Java OpenTracing Instrumentation
Datadog integrates with the OpenTracing API.
Setup
For Maven, add this to pom.xml
:
<!-- OpenTracing API -->
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>0.32.0</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
<version>0.32.0</version>
</dependency>
<!-- Datadog API -->
<dependency>
<groupId>com.datadoghq</groupId>
<artifactId>dd-trace-api</artifactId>
<version>${dd-trace-java.version}</version>
</dependency>
<!-- Datadog OpenTracing bridge (only needed if you do not use dd-java-agent for autoinstrumentation) -->
<dependency>
<groupId>com.datadoghq</groupId>
<artifactId>dd-trace-ot</artifactId>
<version>${dd-trace-java.version}</version>
</dependency>
For Gradle, add:
compile group: 'io.opentracing', name: 'opentracing-api', version: "0.32.0"
compile group: 'io.opentracing', name: 'opentracing-util', version: "0.32.0"
compile group: 'com.datadoghq', name: 'dd-trace-api', version: "${dd-trace-java.version}"
// Datadog OpenTracing bridge (only needed if you do not use dd-java-agent for autoinstrumentation)
compile group: 'com.datadoghq', name: 'dd-trace-ot', version: "${dd-trace-java.version}"
Configure your application using environment variables or system properties as discussed in the configuration section.
If you’re not using autoinstrumentation, you must register a configured tracer with GlobalTracer
. For this, call GlobalTracer.register(DDTracer.builder().build())
early on in your application startup (for example, main method).
import datadog.opentracing.DDTracer;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
public class Application {
public static void main(String[] args) {
DDTracer tracer = DDTracer.builder().build();
GlobalTracer.register(tracer);
// register the same tracer with the Datadog API
datadog.trace.api.GlobalTracer.registerIfAbsent(tracer);
}
}
Aside from environment variables and system properties, there are additional configuration options as part of the DDTracer.Builder
interface. Consult the Javadoc for a full listing.
Note: Never add dd-java-agent
to your classpath. Doing so can cause unexpected behavior.
Asynchronous traces
An asynchronous trace is when a span is started in one thread and finished in another. To instrument this behavior, a new scope must be used in each thread the span is active.
// Step 1: start the Scope/Span on the work submission thread
final Tracer tracer = GlobalTracer.get();
final Span span = tracer.buildSpan("ServicehandlerSpan").start();
try (final Scope scope = tracer.activateSpan(span)) {
// submission thread impl...
submitAsyncTask(new Runnable() {
@Override
public void run() {
// Step 2: reactivate the Span in the worker thread
try (final Scope scope = tracer.activateSpan(span)) {
// worker thread impl
} finally {
// Step 3: Finish Span when work is complete
span.finish();
}
}
});
}
Inject and extract context for distributed tracing
Create a distributed trace using manual instrumentation with OpenTracing:
// Step 1: Inject the Datadog headers in the client code
Span span = tracer.buildSpan("httpClientSpan").start();
try (Scope scope = tracer.activate(span)) {
HttpRequest request = /* your code here */;
tracer.inject(span.context(),
Format.Builtin.HTTP_HEADERS,
new MyHttpHeadersInjectAdapter(request));
// http request impl...
} finally {
span.finish();
}
public static class MyHttpHeadersInjectAdapter implements TextMap {
private final HttpRequest httpRequest;
public HttpHeadersInjectAdapter(final HttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
@Override
public void put(final String key, final String value) {
httpRequest.addHeader(key, value);
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
throw new UnsupportedOperationException("This class should be used only with tracer#inject()");
}
}
// Step 2: Extract the Datadog headers in the server code
HttpRequest request = /* your code here */;
final SpanContext extractedContext =
GlobalTracer.get().extract(Format.Builtin.HTTP_HEADERS,
new MyHttpRequestExtractAdapter(request));
// is a child of http client span in step 1
Span span = tracer.buildSpan("httpServerSpan").asChildOf(extractedContext).start();
try (Scope scope = tracer.activateSpan(span)) {
// http server impl...
} finally {
span.finish();
}
public class MyHttpRequestExtractAdapter implements TextMap {
private final HttpRequest request;
public HttpRequestExtractAdapter(final HttpRequest request) {
this.request = request;
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return request.headers().iterator();
}
@Override
public void put(final String key, final String value) {
throw new UnsupportedOperationException("This class should be used only with Tracer.extract()!");
}
}
Notice the above examples only use the OpenTracing classes. Check the OpenTracing API for more details and information.