opentracing-java
OpenTracing API for Java. 🛑 This library is DEPRECATED! https://github.com/opentracing/specification/issues/163
Top Related Projects
Java distributed tracing implementation compatible with Zipkin backend services.
APM, Application Performance Monitoring System
Zipkin is a distributed tracing system
Quick Overview
OpenTracing-Java is the Java implementation of the OpenTracing API, which provides a vendor-neutral, open standard for distributed tracing. It allows developers to instrument their applications for distributed tracing without tying them to any particular tracing system implementation.
Pros
- Vendor-neutral: Can be used with various tracing systems (Jaeger, Zipkin, etc.)
- Easy to integrate: Provides simple APIs for instrumenting applications
- Extensible: Supports custom span tags and log fields
- Wide ecosystem support: Many frameworks and libraries offer OpenTracing integration
Cons
- Deprecated: OpenTracing has been merged into OpenTelemetry, which is now the recommended standard
- Limited feature set: Some advanced tracing features may not be available
- Potential performance overhead: Instrumentation can add some overhead to application performance
- Maintenance concerns: As a deprecated project, it may receive fewer updates and improvements
Code Examples
- Creating a simple span:
Tracer tracer = GlobalTracer.get();
Span span = tracer.buildSpan("operation-name").start();
try {
// Your code here
} finally {
span.finish();
}
- Adding tags to a span:
Span span = tracer.buildSpan("http-request").start();
span.setTag("http.method", "GET");
span.setTag("http.url", "https://example.com");
- Creating a child span:
Span parentSpan = tracer.activeSpan();
Span childSpan = tracer.buildSpan("child-operation")
.asChildOf(parentSpan)
.start();
Getting Started
To use OpenTracing in your Java project:
- Add the dependency to your
pom.xml
(for Maven):
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>0.33.0</version>
</dependency>
- Initialize a tracer (example using Jaeger):
import io.jaegertracing.Configuration;
import io.opentracing.Tracer;
public class TracerConfig {
public static Tracer initTracer(String serviceName) {
Configuration.SamplerConfiguration samplerConfig = Configuration.SamplerConfiguration.fromEnv().withType("const").withParam(1);
Configuration.ReporterConfiguration reporterConfig = Configuration.ReporterConfiguration.fromEnv().withLogSpans(true);
Configuration config = new Configuration(serviceName).withSampler(samplerConfig).withReporter(reporterConfig);
return config.getTracer();
}
}
- Set the global tracer:
import io.opentracing.util.GlobalTracer;
Tracer tracer = TracerConfig.initTracer("your-service-name");
GlobalTracer.registerIfAbsent(tracer);
Now you can use the tracer throughout your application using GlobalTracer.get()
.
Competitor Comparisons
Java distributed tracing implementation compatible with Zipkin backend services.
Pros of Brave
- More mature and widely adopted in production environments
- Offers built-in integration with Zipkin for distributed tracing
- Provides a simpler API with less configuration required
Cons of Brave
- Less flexible than OpenTracing for custom implementations
- Tied more closely to the Zipkin ecosystem, potentially limiting interoperability
- May have a steeper learning curve for developers new to tracing concepts
Code Comparison
Brave:
Tracing tracing = Tracing.newBuilder()
.localServiceName("my-service")
.spanReporter(spanReporter)
.build();
Tracer tracer = tracing.tracer();
OpenTracing:
Tracer tracer = new CustomTracerImpl();
GlobalTracer.registerIfAbsent(tracer);
Brave focuses on simplicity and integration with Zipkin, while OpenTracing provides a more abstract and flexible approach. Brave's API is generally more concise, but OpenTracing offers greater extensibility for custom implementations. Both libraries serve the purpose of distributed tracing, but cater to different use cases and preferences in terms of ecosystem integration and API design.
APM, Application Performance Monitoring System
Pros of SkyWalking
- More comprehensive observability solution, including APM, metrics, and logging
- Built-in UI for visualization and analysis
- Supports multiple languages and frameworks out of the box
Cons of SkyWalking
- Heavier and more complex to set up compared to OpenTracing
- Steeper learning curve for full utilization of features
- Less flexibility for custom instrumentation
Code Comparison
SkyWalking agent configuration:
-javaagent:/path/to/skywalking-agent.jar
-Dskywalking.agent.service_name=your-service-name
-Dskywalking.collector.backend_service=localhost:11800
OpenTracing initialization:
Tracer tracer = new CustomTracer();
GlobalTracer.registerIfAbsent(tracer);
Span span = tracer.buildSpan("operation-name").start();
try (Scope scope = tracer.activateSpan(span)) {
// Your code here
}
SkyWalking provides a more automated approach to instrumentation, while OpenTracing requires manual span creation and management. SkyWalking offers a broader set of features but at the cost of increased complexity, whereas OpenTracing focuses on providing a standardized API for distributed tracing with greater flexibility for custom implementations.
Zipkin is a distributed tracing system
Pros of Zipkin
- Full-featured distributed tracing system with built-in UI and storage options
- Supports multiple languages and frameworks out-of-the-box
- Provides a complete ecosystem for trace collection, storage, and visualization
Cons of Zipkin
- More complex setup and configuration compared to OpenTracing
- Less flexibility in terms of instrumentation and custom implementations
- Steeper learning curve for developers new to distributed tracing
Code Comparison
Zipkin instrumentation example:
Span span = tracer.newTrace().name("encode").start();
try {
doSomethingExpensive();
} finally {
span.finish();
}
OpenTracing instrumentation example:
Span span = tracer.buildSpan("encode").start();
try (Scope scope = tracer.activateSpan(span)) {
doSomethingExpensive();
} finally {
span.finish();
}
Both examples show similar syntax for creating and finishing spans, but OpenTracing provides a more explicit scope management approach. Zipkin's implementation is slightly more concise, while OpenTracing offers more control over span activation and propagation.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
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.
Within-process propagation and the Scope
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
along with a Scope
to signal deactivation. Any Span
may be transferred to another callback or thread, but not Scope
; more on this below.
Accessing the active Span
Access to the active span is straightforward:
io.opentracing.Tracer tracer = ...;
...
Span span = tracer.scopeManager().activeSpan();
if (span != null) {
span.log("...");
}
Starting a new Span
The common case starts a Span
and then sets it as the active instance via ScopeManager
:
io.opentracing.Tracer tracer = ...;
...
Span span = tracer.buildSpan("someWork").start();
try (Scope scope = tracer.scopeManager().activate(span)) {
// 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();
}
If there is already an active Span
, 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 = ...;
...
Span span = tracer.buildSpan("someWork").ignoreActiveSpan().start();
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:
- Start a
Span
viastart
. - At the beginning of the closure/
Runnable
/Future
/etc itself, invoketracer.scopeManager().activate(span)
to re-activate theSpan
and get a newScope
, thenclose()
it when theSpan
is no longer active (or use try-with-resources for less typing). - Invoke
span.finish()
when the work is done.
Here is an example using CompletableFuture
:
io.opentracing.Tracer tracer = ...;
...
// STEP 1 ABOVE: start the Span.
final Span span = tracer.buildSpan("ServiceHandlerSpan").start();
try (Scope scope = tracer.scopeManager().activate(span)) {
// Do work.
...
future = CompletableFuture.supplyAsync(() -> {
// STEP 2 ABOVE: reactivate the Span in the callback.
try (Scope scope = tracer.scopeManager().activate(span)) {
...
}
}).thenRun(() -> {
// STEP 3 ABOVE: finish the Span when the work is done.
span.finish();
});
}
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.
Deprecated members since 0.31
ScopeManager.active(Span, boolean)
and SpanBuilder.startActive()
have been deprecated as part of removing automatic Span
finish upon Scope
close, as doing it through try-with statements would make it hard to properly handle errors (Span
objects would get finished before a catch block would be reached).
This improves API safety, and makes it more difficult to do the wrong thing and end up with unexpected errors.
Scope.span()
and ScopeManager.scope()
have been deprecated in order to prevent the anti-pattern of passing Scope
objects between threads (Scope
objects are not guaranteed to be thread-safe).
Now Scope
will be responsible for Span
deactivation only, instead of being a Span
container.
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
Contributing
See Contributing for matters such as license headers.
Top Related Projects
Java distributed tracing implementation compatible with Zipkin backend services.
APM, Application Performance Monitoring System
Zipkin is a distributed tracing system
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot