6.0 KiB
OpenTracing API
The OpenTracing group offers an API to instrument your code. This document is a kind of a "quick start" for the official specification: https://github.com/opentracing/specification
There are several concepts exposed by the OpenTracing API:
- The core API used for instrumenting the code
- The tracer implementations are in charge of capturing and reporting the traces. For instance
dd-trace
generates and reports traces to the Datadog trace agent. - In-process trace propagation for trace consistency in a concurrent/asynchronous request context.
- Distributed trace propagation for when receiving a request and making external calls.
OpenTracing Core API
Official documentation link: Opentracting Tracer specification
The core API exposes 3 main objects:
Tracers
The tracer is in charge of instantiating new spans for a given context, and sending them to the appropriate sink when complete.
The tracer instantiation depends of the implementation you chose. For instance, dd-trace
allows you
to send the traces to a logger or directly to a running Datadog agent.
// Initialize the Datadog Java Tracer to write traces to the log:
Tracer tracer = new DDTracer();
After a tracer is created, you will usually want to register it with the GlobalTracer
to make it accessible all OpenTracing instrumentation in your JVM.
io.opentracing.util.GlobalTracer.register(tracer);
Spans
Once a tracer is instantiated, you can use it to create and manage span. OpenTracing defines a SpanBuilder
accessible through the method buildSpan(String operationName)
to serve this purpose.
// Create a new Span with the operation name "componentTracking"
ActiveSpan current = tracer.buildSpan("componentTracking").startActive(true);
This example creates a simple span referenced "componentTracking". The startActive()
method starts a new span and sets it
as the active span. This means that any new span created going forward on the same thread will reference this as its' parent.
If another span is already active, the new span will replace it.
A collection of related spans is called a trace.
Sometimes you need to create a span without promoting it as the active. If you want to do that, use the startManual()
method instead.
// Create a span, but do not promoting it as the active span
Span anotherSpan = tracer.buildSpan("componentTracking").start();
Typically, span creations are made in the beginning of the methods you want to trace.
And of course, you need to finish/close the span in order to get the operation duration.
This is achieved using the finish
method.
// Finishing the tracing operation
current.finish()
Be careful!! All children spans must be finished/closed before finishing the root span. If child spans are unfinished when the parent attempts to finish, the span will remain incomplete and risk being unreported.
Now, you are able to create, start and stop very simple spans. You can manipulate them at any time and add extra contextual information using the tags.
Tags
Tags are local to each span and no tags will be inherited from the parent. Information relevant to all spans in a trace should be stored as baggage.
OpenTracing defines a standard set of tags and should be used appropriately. Custom tags can also be defined as needed.
// Create a span and set it as the active span
ActiveSpan valuableSpan = tracer.
buildSpan("componentTracking")
.withTag("custom-meta", "some-useful-value")
.withTag(Tags.COMPONENT, "my-component-mysql")
.startActive(true);
// Somewhere further in the code
Tags.HTTP_URL.setTag(valuableSpan, "https://my-endpoint/resource/item");
Tags.HTTP_STATUS.setTag(valuableSpan, 200);
Baggage
Information relevant for the entire trace is stored as baggage. Baggage is very similar to tags, but has important distinctions. Baggage is:
- Associated with all spans for a trace.
- Propagated outside the trace context via HTTP or Messaging protocols (depends of the tracer implementation).
// Like tags, you can add baggage item to the span
valuableSpan.setBaggageItem("username", "modernmajorgeneral");
Errors
Errors are manually captured in a span by setting the error flag and logging the error attributes.
try {
// your code
} catch (Exception e) {
// capture error
ActiveSpan span = GlobalTracer.get().activeSpan();
Tags.ERROR.set(span, Boolean.TRUE);
span.log(Collections.singletonMap(io.opentracing.log.Fields.ERROR_OBJECT, e));
// recovery code
}
OpenTracing Asynchronous API
An ActiveSpan
can generate a Continuation
as a way of propagating traces from across a thread boundary.
On the parent thread:
Continuation spanContinuation = valuableSpan.capture();
// pass the continuation onto a new thread
On a different thread:
ActiveSpan valuableSpan = spanContinuation.activate();
// span is now active on the new thread
OpenTracing Cross Process Propagation
Spans are associated across processes in a trace via the Tracer.extract
and Tracer.inject
methods.
// On the start of a new trace in an application, associate incoming request with existing traces.
SpanContext spanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, someTextMapInstance);
ActiveSpan currentSpan = tracer.buildSpan("componentTracking").asChildOf(spanCtx).startActive(true);
// When making an external call propagate the trace by injecting it into the carrier...
tracer.inject(currentSpan.context(), Format.Builtin.HTTP_HEADERS, someTextMapInstance);