Giter Site home page Giter Site logo

opentracing-java's Introduction

Build Status Coverage Status Released Version

OpenTracing API for Java

This library is a Java platform API for OpenTracing.

Required Reading

In order to understand the Java platform API, one must first be familiar with the OpenTracing project and terminology more specifically.

Usage

Initialization

Initialization is OpenTracing-implementation-specific. Generally speaking, the pattern is to initialize a Tracer once for the entire process and to use that Tracer for the remainder of the process lifetime. It is a best practice to set the GlobalTracer, even if also making use of cleaner, more modern dependency injection. (See the next section below for rationale)

Accessing the Tracer

Where possible, use some form of dependency injection (of which there are many) to access the Tracer instance. For vanilla application code, this is often reasonable and cleaner for all of the usual DI reasons.

That said, instrumentation for packages that are themselves statically configured (e.g., JDBC drivers) may be unable to make use of said DI mechanisms for Tracer access, and as such they should fall back on GlobalTracer. By and large, OpenTracing instrumentation should always allow the programmer to specify a Tracer instance to use for instrumentation, though the GlobalTracer is a reasonable fallback or default value.

Scopes and within-process propagation

For any thread, at most one Span may be "active". Of course there may be many other Spans involved with the thread which are (a) started, (b) not finished, and yet (c) not "active": perhaps they are waiting for I/O, blocked on a child Span, or otherwise off of the critical path.

It's inconvenient to pass an active Span from function to function manually, so OpenTracing requires that every Tracer contains a ScopeManager that grants access to the active Span through a Scope. Any Span may be transferred to another callback or thread, but not Scope; more on this below.

Accessing the active Span through Scope

Access to the active span is straightforward:

io.opentracing.Tracer tracer = ...;
...
Scope scope = tracer.scopeManager().active();
if (scope != null) {
    scope.span().log("...");
}

Starting a new Span

The common case starts a Scope that's automatically registered for intra-process propagation via ScopeManager.

Note that startActive(true) finishes the span on Scope.close(). Use it carefully because the try-with-resources construct finishes the span before the catch or finally blocks are executed, which makes logging exceptions and setting tags impossible. It is recommended to start the span and activate it later in try-with-resources. This makes the span available in catch and finally blocks.

io.opentracing.Tracer tracer = ...;
...
Span span = tracer.buildSpan("someWork").start();
try (Scope scope = tracer.scopeManager().activate(span, false)) {
    // Do things.
} catch(Exception ex) {
    Tags.ERROR.set(span, true);
    span.log(Map.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage()));
} finally {
    span.finish();
}

The following code uses try-with-resources to finish the span.

io.opentracing.Tracer tracer = ...;
...
try (Scope scope = tracer.buildSpan("someWork").startActive(true)) {
    // Do things.
    //
    // `scope.span()` allows us to pass the `Span` to newly created threads.
} catch(Exception ex) {
    // cannot record the exception in the span since scope is not accessible and span is finished
}

If there is a Scope, it will act as the parent to any newly started Span unless the programmer invokes ignoreActiveSpan() at buildSpan() time or specified parent context explicitly:

io.opentracing.Tracer tracer = ...;
...
Scope scope = tracer.buildSpan("someWork").ignoreActiveSpan().startActive();

Deferring asynchronous work

Consider the case where a Span's lifetime logically starts in one thread and ends in another. For instance, the Span's own internal timing breakdown might look like this:

 [ ServiceHandlerSpan                                 ]
 |·FunctionA·|·····waiting on an RPC······|·FunctionB·|
            
---------------------------------------------------------> time

The "ServiceHandlerSpan" is active while it's running FunctionA and FunctionB, and inactive while it's waiting on an RPC (presumably modelled as its own Span, though that's not the concern here).

The ScopeManager API makes it possible to fetch the span() in FunctionA and re-activate it in FunctionB. Note that every Tracer contains a ScopeManager. These are the steps:

  1. Start a Span via either startManual or startActive(false) to prevent the Span from being finished upon Scope deactivation.
  2. In the closure/Runnable/Future/etc itself, invoke tracer.scopeManager().activate(span, false) to re-activate the Span and get a new Scope, then deactivate() it when the Span is no longer active (or use try-with-resources for less typing).
  3. In the closure/Runnable/Future/etc where the end of the task is reached, invoke tracer.scopeManager().activate(span, true) to re-activate the Span and have the new Scope close the Span automatically.

For example:

io.opentracing.Tracer tracer = ...;
...
// STEP 1 ABOVE: start the Scope/Span
try (Scope scope = tracer.buildSpan("ServiceHandlerSpan").startActive(false)) {
    ...
    final Span span = scope.span();
    doAsyncWork(new Runnable() {
        @Override
        public void run() {

            // STEP 2 ABOVE: reactivate the Span in the callback, passing true to
            // startActive() if/when the Span must be finished.
            try (Scope scope = tracer.scopeManager().activate(span, false)) {
                ...
            }
        }
    });
}

Observe that passing Scope to another thread or callback is not supported. Only Span can be used under this scenario.

In practice, all of this is most fluently accomplished through the use of an OpenTracing-aware ExecutorService and/or Runnable/Callable adapter; they factor out most of the typing.

Compatibility with Opentracing 0.30

For users supporting instrumentation code using Opentracing 0.30, there is a 0.30 compatibility package that can be used to wrap a 0.31 Tracer and expose it as a 0.30 Tracer:

io.opentracing.Tracer upstreamTracer = ...;
io.opentracing.v_030.Tracer tracer = new TracerShim(upstreamTracer);
try (ActiveSpan span = tracer.buildSpan("ServiceHandlerSpan").startActive()) {
   ...
}

Instrumentation Tests

This project has a working design of interfaces for the OpenTracing API. There is a MockTracer to facilitate unit-testing of OpenTracing Java instrumentation.

Packages are deployed to Maven Central under the io.opentracing group.

Development

This is a maven project, and provides a wrapper, ./mvnw to pin a consistent version. Run ./mvnw clean install to build, run tests, and create jars.

This wrapper was generated by mvn -N io.takari:maven:wrapper -Dmaven=3.5.0

License

Apache 2.0 License.

Contributing

See Contributing for matters such as license headers.

opentracing-java's People

Contributors

bhs avatar pavolloffay avatar michaelsembwever avatar bensigelman avatar sjoerdtalsma avatar adriancole avatar wu-sheng avatar yurishkuro avatar objectiser avatar malafeev avatar carlosalberto avatar natehart avatar sjhewitt avatar felixbarny avatar sudoshweta avatar oibe avatar kratz74 avatar wsargent avatar tedsuo avatar sebsandberg avatar mikegoldsmith avatar mcumings avatar jpkrohling avatar elgris avatar hypnoce avatar bhanafee avatar devinsba avatar realark avatar

Watchers

James Cloos avatar AtingTot avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.