What is opentracing?
The opentracing npm package provides a standard, vendor-neutral API for distributed tracing. It allows developers to add instrumentation to their applications, which is essential for understanding the performance and behavior of complex distributed systems. The package enables the tracking of request flows across various services and systems, making it easier to diagnose and optimize performance issues.
What are opentracing's main functionalities?
Starting a new trace
This code demonstrates how to start a new trace with a span named 'my_span' and then finish the span. This is the basic building block of distributed tracing.
const opentracing = require('opentracing');
const tracer = opentracing.globalTracer();
const span = tracer.startSpan('my_span');
span.finish();
Injecting and extracting span context
This code snippet shows how to inject a span context into a carrier (e.g., HTTP headers) and then extract it. This is crucial for propagating trace context across process boundaries.
const opentracing = require('opentracing');
const tracer = opentracing.globalTracer();
const spanContext = span.context();
const carrier = {};
tracer.inject(spanContext, opentracing.FORMAT_HTTP_HEADERS, carrier);
const extractedContext = tracer.extract(opentracing.FORMAT_HTTP_HEADERS, carrier);
Setting tags and logs
This example illustrates how to set tags and logs on a span. Tags are key-value pairs that provide additional context, such as HTTP method, while logs record timed events within a span.
const opentracing = require('opentracing');
const span = opentracing.globalTracer().startSpan('my_span');
span.setTag(opentracing.Tags.HTTP_METHOD, 'GET');
span.log({ event: 'request_sent' });
span.finish();
Other packages similar to opentracing
jaeger-client
Jaeger Client is an open-source, end-to-end distributed tracing system that works with the OpenTracing API. It is similar to opentracing but is specifically designed to work with the Jaeger backend. It provides more specific implementations for trace collection and reporting.
zipkin
Zipkin is another distributed tracing system that captures timing data needed to troubleshoot latency problems in service architectures. It has a different API compared to opentracing but serves a similar purpose in tracing requests through distributed systems.
lightstep-tracer
LightStep Tracer is a distributed tracing library that implements the OpenTracing API, similar to opentracing. It is designed to integrate with LightStep's real-time analysis platform, providing more advanced features and integrations for monitoring and diagnosing distributed systems.
OpenTracing API for JavaScript
This library is a JavaScript implementation of Open Tracing API. It is intended for use both on the server and in the browser.
Required Reading
To fully understand this platform API, it's helpful to be familiar with the OpenTracing project and
terminology more generally.
Quick Start
Install the package:
npm install --save opentracing
In your JavaScript code, add instrumentation to the operations to be tracked. This is composed primarily of using "spans" around operations of interest and adding log statements to capture useful data relevant to those operations.
var http = require('http');
var Tracer = require('opentracing');
var span = Tracer.startSpan('http_request');
var opts = {
host : 'example.com',
method: 'GET',
port : '80',
path: '/',
};
http.request(opts, function (res) {
res.setEncoding('utf8');
res.on('error', function (err) {
span.logEvent('request_error', err);
span.finish();
});
res.on('data', function (chunk) {
span.logEvent('data_received', chunk);
});
res.on('end', function(err) {
span.logEvent('request_end', err);
span.finish();
});
}).end();
The default behavior of the opentracing
package is to act as a "no-op" implementation.
To capture and make the tracing data actionable, the Tracer
object should be initialized with the OpenTracing implementation of your choice as in the pseudo-code below:
var Tracer = require('opentracing');
var TracingBackend = require('tracing-implementation-of-your-choice');
Tracer.initGlobalTracer(TracingBackend.create());
Note: the underlying implementation object is shared between all inclusions of the opentracing
package, so initGlobalTracer
needs to only be called once during initialization.
API Documentation
There is a hosted copy of the current generated ESDoc API Documentation here.
Notes on backwards-incompatible changes
v0.9.x to v0.10.x
This release makes the opentracing-javascript
package conformant with the ideas proposed in opentracing/opentracing.github.io#99. The API changes can be summarized as follows:
- Every
Span
has a SpanContext
, available via Span.context()
. The SpanContext
represents the subset of Span
state that must propagate across process boundaries in-band along with the application data. Span.setBaggageItem()
and Span.getBaggageItem()
have moved to SpanContext
. Calls can be migrated trivially: Span.context().{set,get}BaggageItem()
.- The first parameter to
Tracer.inject()
is now a SpanContext
. As a convenience, a Span
may be passed instead. - There is a new concept called a
Reference
; a reference describes a relationship between a newly started Span
and some other Span
(via a SpanContext
). The common case is to describe a reference to a parent Span
that depends on the child Span
('REFERENCE_CHILD_OF`). Tracer.startSpan(operation, fields)
no longer accepts fields.parent
; it now accepts either fields.childOf
, a SpanContext
or Span
instance, or fields.references
, an array of one or more Reference
objects. The former is just a shorthand for the latter.Tracer.join(operationName, format, carrier)
has been removed from the API. In its place, use Tracer.extract(format, carrier)
which returns a SpanContext
, and pass that SpanContext
as a reference in Tracer.startSpan()
.
TL;DR, to start a child span, do this:
let parentSpan = ...;
let childSpan = Tracer.startSpan('child op', { childOf : parentSpan });
... and to continue a trace from the server side of an RPC, do this:
let format = ...;
let carrier = ...;
let extractedCtx = Tracer.extract(format, carrier);
let serverSpan = Tracer.startSpan('...', { childOf : extractedCtx });
Development Information
I.e., information for developers working on this package.
Building the library
make build
creates the compiled, distributable codemake test
runs the tests
JavaScript OpenTracing Implementations
I.e. information for developers wanting to create an OpenTracing-compatible JavaScript implementation.
The API layer uses a bridge pattern to pass work to the specific tracing implementation. The indirection allows the API layer to enforce greater API conformance and standardization across implementations (especially in debug builds), which helps keep instrumented code more portable across OpenTracing implementations.
The "implementation API" - i.e. the interface the API layer expects to be able to call on the implementation - is a proper subset of the API layer itself. The surface area of the implementation API has been reduced in the case where the an API layer method (usually a convenience method of some form) can be expressed in terms of another more general method. For example, logEvent
can be expressed as a log
call, therefore the implementation only needs to implement log
.
For truly implementation-dependent methods, the JavaScript API layer does expose imp()
methods on each major type to allow the implementations to be accessed directly. Use of implementation-dependent methods is discouraged as it immediately makes instrumented code no longer portable. However, the imp()
call does at least call attention to deviations from the standard API without making implementation-dependent calls impossible.