GithubHelp home page GithubHelp logo

opentracing / opentracing-java Goto Github PK

View Code? Open in Web Editor NEW
1.7K 95.0 345.0 905 KB

OpenTracing API for Java. 🛑 This library is DEPRECATED! https://github.com/opentracing/specification/issues/163

Home Page: http://opentracing.io

License: Apache License 2.0

Shell 2.08% Java 97.92%

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.

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:

  1. Start a Span via start.
  2. At the beginning of the closure/Runnable/Future/etc itself, invoke tracer.scopeManager().activate(span) to re-activate the Span and get a new Scope, then close() it when the Span is no longer active (or use try-with-resources for less typing).
  3. 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

Apache 2.0 License.

Contributing

See Contributing for matters such as license headers.

opentracing-java's People

Contributors

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

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

opentracing-java's Issues

Identical packages in opentracing-api and opentracing-noop cause build failure with JDK 9 + jigsaw

Maven dependency on opentracing-util required to use GlobalTracer.get() introduced transitive dependency on opentracing-noop and opentracing-api.
Unfortunately both opentracing-noop and opentracing-api contains the same package "io.opentracing" and this causes error in JDK 9 + jigsaw projects containing module-info.java.

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] module <module_name> reads package io.opentracing from both opentracing.api and opentracing.noop
[ERROR] module reads package io.opentracing from both opentracing.noop and opentracing.api
[ERROR] module opentracing.api reads package io.opentracing from both opentracing.noop and opentracing.api
[ERROR] module opentracing.util reads package io.opentracing from both opentracing.noop and opentracing.api
[ERROR] module opentracing.noop reads package io.opentracing from both opentracing.noop and opentracing.api

It would be good to rename "io.opentracing" package in opentracing-noop to something like "io.opentracing.noop"

Duration of a Span

I have been following open tracing for quite a while now and am excited about what is being done!

In this issue, I would like to discuss about the duration of a Span. In AbstractSpanBuilder, start is initialized, but this value is not used.

Tests utilizing this class, such as TestSpanBuilder define start to be when the Span is created.

Should the duration of a Span start at the creation of the Span object or the SpanBuilder object? I assumed the former based on the tests, but the initialization in AbstractSpanBuilder seems a bit misleading.

Create dummy user for travis deploys

My user being in the commit log during releases is confusing, as it is easy to assume I'm doing the releases! The travis yaml could be re-encrypted with a dummy account.

Introduce carrier format identifiers for parity with other platforms

This is driven by several concerns:

  1. The implementation to locate the appropriate injectors/extractors based on runtime information of the passed-in carrier type is ugly and inefficient. Having a unique identifier for a format, like opentracing.TextMap in Go, makes the implementation as simple as a map lookup, and remove ambiguity of dealing with potentially multiple superclasses and interfaces implemented by a carrier
  2. It is currently impossible to reuse the same carrier for different formats. For example, the TextMapReader/Writer interfaces in Go can be used for both plain TextMap format as well as for upcoming HTTPHeader format. It is much easier to define another constant for the format than to duplicate the reader/writer interfaces.
  3. One earlier concern was that having both a format and the carrier creates a possibility of type mismatches. The proposal below addresses that at compile time.
interface TextMapReader {}
interface TextMapWriter {}

interface Format<R, W> {}

public class Formats<R, W> implements Format<R, W> {
    public final static Format<TextMapReader, TextMapWriter> TEXT_MAP = new Formats<>();
    public final static Format<TextMapReader, TextMapWriter> HTTP_HEADER = new Formats<>();
    public final static Format<ByteBuffer, ByteBuffer> BINARY= new Formats<>();
}

interface Span {}
interface Tracer {
    <W> void inject(Span span, Format<?, W> format, W carrier);
    <R> Span extract(Format<R, ?> format, R carrier);
}

public void test(Tracer tracer) {
    Span span = tracer.extract(Formats.TEXT_MAP, new TextMapReader(){});
    tracer.inject(span, Formats.TEXT_MAP, new TextMapWriter(){});
}

Make GlobalTracer easier to use with java 8 (depends on #115)

In #115 GlobalTracer gets a new method:

* @return the {@link ActiveSpan active span}, or null if none could be found.
*/
ActiveSpan activeSpan();

One of the common uses of this method will be adding tags/logs to the active span if present, like this:

if(GlobalTracer.activeSpan() != null) {
   GlobalTracer.activeSpan().setTag("tagged", true);
} 

It's tempting to declare the return type as Optional and instead be able to do this:

GlobalTracer.activeSpan().ifPresent(span -> {
    span.setTag("tagged", true)
});

But I guess we can't use java 8 in this project. So instead I propose one of two options:

  1. Add a method taking a Runnable-like interface (something which allows throwing exceptions maybe?) and make this possible:
    GlobalTracer.withActiveSpan(span -> span.setTag("tagged", true))
    
    I'm not sure about the naming
  2. Implement simplified Optional<ActiveSpan> and return that. activeSpan() comes from ActiveSpanSource and I'm not sure if it should be touched, so it would need to be returned from a new method.

I'd go with option 1.

Mechanism to understand when ActiveSpan has been finished

Currently, if writing a wrapper tracer, there is no way to know when the ActiveSpan has actually been finished, as the application (or framework integrations) simply call deactivate, but as this is ref counted, any call to this method could potentially finish the span.

Options are:
a) The wrapper tracer would also need to reference count
b) The deactivate method could return a boolean indicating whether the associated ActiveSpan has been finished
c) The BaseSpan could support an isFinished method which could be checked after deactivate
d) Implement some active span lifecycle support

MockTracer custom recorders

Hello,

I would like to contribute a custom recorder for MockTracer. In the most cases recording spans to the list is sufficient, however, it would be good to be able to send spans to an arbitrary recorder (e.g. console for demo purposes).

interface Recorder {
    void record(Span span);
}

....
public MockTracer() {
 this(Propagator.PRINTER, Recorder.LIST)
}

public MockTracer(Propagator propagator, Recorder recorder) {
}

@adriancole @bensigelman @yurishkuro what do you think?

Discrepancy: Join can create a new trace in Java, but not in Go/Python

Documentation for Join method says

// If the carrier object has no such span stored within it, a new Span is created.

This is different from Go/Python implementations where Join operation results in an error if the incoming request contains no trace context. Java versions provides no hook for instrumentation to decide if it wants a new trace started if one didn't come from upstream. On the other hand, it makes instrumentation code shorter by not forcing the end user to do:

try {
    tracer.join(...)
} catch () {
    tracer.startSpan(...)
} 

I think we should be consistent in the semantics of Join operation across languages.

@bensigelman @michaelsembwever @oibe

Add a new Binary Format to allow for variable SpanContext size

A Binary format is defined in opentracing-api which uses a ByteBuffer as a carrier. ByteBuffer needs to be allocated with a predetermined size for the SpanContext to be injected. This in turn requires a size calculation of baggage.

Possible approaches:

  1. Place an upper limit on the size of the baggage.
  2. Define a new carrier class (similar to TextMap) and format (similar to BINARY). Essentially, this would provide a wrapper for byte[].
// Additional Format specification (Format.java)
public final static Format<SimpleBinary> SIMPLE_BINARY = new Builtin<SimpleBinary>();
public class SimpleBinary {
    private byte[] payload;

    public byte[] getPayload() {
        return payload;
    }

    public void setPayload(byte[] payload) {
        this.payload = payload;
    }
}

BinaryInjectorImpl and BinaryExtractorImpl would be library implementation-specific (below is an example)

final class BinaryInjectorImpl implements Injector<SimpleBinary> {

    private final AbstractTracer tracer;
    private boolean baggageEnabled = AbstractTracer.BAGGAGE_ENABLED;

    BinaryInjectorImpl(AbstractTracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public void inject(SpanContext spanContext, SimpleBinary carrier) {
        byte[] contextBytes = convertSpanContextToBytes(spanContext);
        carrier.setPayload(contextBytes);
    }

    void setBaggageEnabled(boolean baggageEnabled) {
        this.baggageEnabled = baggageEnabled;
    }
}

This allows the library to not have to calculate the size of the SpanContext as this size will depend on how the context is converted to bytes in convertSpanContextToBytes (ie using avro, etc).

Open Tracing API and JUL Logging

I'm a developer on the Trace Compass trace analyzer/visualizer project. We started using JUL logging in our application and the data we save is such that it is very similar in semantic to the Open Tracing API (we get the same causality between spans and extra data, etc).

I'm looking at the Open Tracing API to see if we can use that instead, if it could simplify the developer's lives.

The advantages of JUL that I see over Open Tracing API are the use of class-specific Loggers (though the SpanContext can replace that maybe). But most importantly, the hability to set the LogLevel of our trace points and configure our logging needs at runtime through a simple properties file. I have the impression that Open Tracing API is a all-or-nothing tracing depending on what library implementation is loaded at runtime. Am I wrong?

Have you thought of a relationship between JUL logging and Open Tracing API? I see the Open Tracing API more like defining what fields/data should be in the message, and JUL remains the vehicle to trace. I guess we could implement a JUL Tracer and use that tracer in our code. Or the other way around: wrap our JUL statements in utility methods that would create the proper OpenTracing API objects, so that we can still take advantage of LogLevels.

Any thoughts on this?

How to understand Opentracing ?

Hi,
In my project,there are two services.

ServiceA is Client ( CS、CR)  and  ServiceB(SS、SR)  is Server,

A send a request to B, B response to A. one request and response.

My question is ,In this scenario,

    (1) If use opentracing  on ServiceA and ServiceB,How many span to build on ServiceA and ServiceB ? only one or two?

    (2) CS、CR、SS、SR ,their spanId  should be the same?

Why does LogData have no getters?

Sorry if this has been discussed before but what is the intended purpose of AbstractSpan.getLogs()? It returns a list of objects which have no getters - so no code can access this data - making that method somewhat useless.

Move Noop tracer to its own module opentracing-noop

Noop tracer is currently in the -impl module, which has the following issues:

  • -impl module was meant to contain an abstract skeleton for tracer implementations
  • Noop tracer is never going to use that skeleton, so bundling it in the same jar increases the end user's dependencies

@bensigelman I know you prefer for it to be in -api, but (a) it's a harder sell, (b) taking a more pedantic view, in the world where everything is DI-ed and "sensible defaults" are considered an anti-pattern (e.g. an echo http server on netty requires 100+ lines of code), an instrumentation of a framework technically does not need the Noop tracer, only the API. So I think it's cleaner to have is as a separate artifact.

Can we define a shared thread-local storage format for Spans?

While explicit context propagation is in many cases the ideal solution, it's not always practical and often requires that people change their code significantly. A thread-local storage (TLS) of the "current span" is widely used approach in various Java tracers. Rather than leaving this issue up for implementations, it would be useful, imo, to define a common mechanism directly in OpenTracing, and increase interoperability between different instrumentations. Specifically, we'd need to define at least two APIs:

  1. saving and retrieving the "current span"
  2. providing a wrapper Runnable / Callable / etc. for carrying the "current span" over thread boundaries

For (1), the two possible approaches are:

  • (1a) a single-span TLS
  • (1b) a stack-like TLS

Some prior discussions on Gitter:

yurishkuro Mar 24 12:33
@bensigelman @dkuebric @adriancole for in-process context propagation in Java, what are your thoughts on what thread-local storage should contain: a single current Span, or a stack of spans?

bensigelman Mar 26 00:02
@yurishkuro sorry for the delay re ^^^, just noticed this! this is a “rich” (i.e., complex) topic, but the short answer IMO is “a single current Span”.

bensigelman Mar 26 00:32
@yurishkuro @dkuebric @michaelsembwever
perhaps more interesting (IMO) is what helpers exist to set and clear that span… One idea is something like the following:

public abstract class TracedRunnable implements Runnable {
    private SpanBuilder childBuilder;
    public TracedRunnable(SpanBuilder childBuilder) {
        this.childBuilder = childBuilder;
    }

    // For subclasses to override… akin to java.lang.Runnable.run().
    public abstract void runInSpan(Span sp);

    // The compatibility bridge to java.lang.Runnable
    public final run() {
        Span parentSpan = Pseudocode.getSpanFromTLS();
        Span childSpan = childBuilder.setParent(parentSpan).start();
        runInSpan(childSpan);
        // We need to reinstate parentSpan before returning from run().
        Pseudocode.setSpanInTLS(parentSpan);
    }
}

Calling code would look approximately like this:

SpanBuilder b = tracer.buildSpan(“child_operation”);
somethingThatNeedsARunnableClosure(arg1, arg2, new SpanRunnable(b) {
    public void runInSpan(Span sp) {
        … do something, etc …
    }
});

PS: @jmacd thoughts/recollections about the above? It's not precisely what I remember from Java dapper, though maybe that’s not a bad thing. :)

yurishkuro Mar 26 18:08
@bensigelman the last line, Pseudocode.setSpanInTLS(parentSpan); is the reason I asked - it works in this simple example (if you add try/finally), but isn't that great in things like Jersey filters, because there are two independent filters, for request and for response. To to be able to reference parentSpan it needs to be stored somewhere else, since TLS slot is occupied by the child span. If TLS slot was instead a stack, then the request filter would do TLS.push(childSpan), and response filter would do child = TLS.pop(); child.end(), thus automatically restoring the parent.
what I don't like about the stack is a possibility of scoping errors, leading to mismatched push/pop

bensigelman Mar 26 22:55
@yurishkuro in the case of something like Jersey, I suppose the Span could be attached to the ContainerRequestContext. But that isn’t satisfying at all… doesn’t seem general enough.
Having thought this through more carefully, I would argue (albeit with hesitation) for something like a stack… every Span would have an optional parent Span, and at finish-time the parent Span would be reinstated into TLS. In the example you gave above, you’d get the behavior you want with TLS as long as a Span is created at Jersey request filter time, and the same span is finished at Jersey response filter time. (It can of course create children of its own, etc, etc)
If the above doesn’t make any sense, maybe there’s some sort of git gist or something we could use as a place to make the problem more concrete?

out of date I think

I think many are calling OpenTracing version 1 or something and it is here 0.20.7 including methods marked deprecated. If these methods are in fact no longer in the api, they should be removed instead of leading people on. If the api is version 1, then it should probably be marked so..

sound sensible?

AbstractSpanBuilder.childOf should not return NoopSpanBuilder

This is in a direct contradiction to the changes done in #56 ("A child of a NoopSpan is a NoopSpan"), but I believe that the behaviour introduced there is not correct.

It seems that there are two uses for Noop implementation:

  1. The default empty tracer implementation
  2. NoopSpanContext might be returned from Tracer.extract when there is no span context in the carrier (instead of null) - to indicate "a lack of"

#56 uses the 2nd meaning of NoopSpanContext and the reasoning is that it makes the fluent builder usable when there is no parent SpanContext - does not require conditional checking for presence:

SpanContext parent = tracer.extract(Format.Builtin.TEXT_MAP, adapter);
try (Span span = tracer.buildSpan("some-name").asChildOf(parent).start()) {
    // server processes request
    return response;
}

The problem is that the behaviour ("A child of a NoopSpan is a NoopSpan") it's not what I expect. I expect that a child of a "no span" (or NoopSpan, or null) is just a span without a parent. And not a NoopSpan, but a Span implementation used by the current tracer.
Why? Because the child span is going to be lost - NoopSpan does not record tags, has empty finish() implementation and the tracer will not be aware of it.

I'm not sure why there is a requirement in brave-opentracing for it to behave this way.

I'd suggest to:

  • always return this inside AbstractSpanBuilder. Something like this:
      @Override
      public final AbstractSpanBuilder asChildOf(SpanContext parent) {
          if(!(parent instanceof NoopSpanContext)) {
              this.addReference(References.CHILD_OF, parent);
          }
          return this;
      }
    
  • add a null check if nulls are not the way to go
  • clearly state what represents an empty span context - in the api (javadoc for Tracer.extract).

Though it might be better to add SpanContext.isEmpty method to the API, make it explicit and get rid of this weird "instanceof".

Adding getters to Span?

In this issue, I want to discuss adding getters to the Span interface. For example

Map<String, Object> getTags();
Object getTag(String key);

Getters for the Logs will probably be a bit more challenging though as there is no LogData interface in the api.

The main reason for me is that it is currently impossible to write a custom reporter which is independent of a particular implementation.

Another reason as mentioned in #48:

One use case where the client might want to access the tags the tracing library has created is when the lib parses the User-Agent header and the client wants to access the information whether the request was initiated from a mobile device.

Move TracerResolver to core java repo util module

As recently happened with the GlobalTracer, I think it would be useful to have the TracerResolver functionality moved from the contrib repo into the core java repo, as it is something users are expecting to be part of the core API.

Realise we may want to work on the functionality in the contrib repo for a while longer, so this issue is a placeholder to register the issue, and collect opinions for and against the move.

OpenTracing encourages classloader-scoped configuration

I find it very strange that in 2017, OpenTracing are encouraging and proliferating classloader-scoped configuration as status quo. Considering all the work in the last decade in JVM configuration, it is surprising that new instrumentation are being made that rely first on the expanding new set of apis like DefaultSpanManager.getInstance(). There are numerous better ways to accomplish the task. For example, why would you configure your service with CDI, Guice, Spring, OSGi, Dagger, yet have it poke around for classloader statics?

The only reasons I can think of for this optimization are:

  • people who don't want to be bothered with integrating with various configuration methodologies, and find it more convenient to route users through static config instead.
  • people here are trying to emulate existing logging frameworks, except loggers are different as they often live far below request (ex at bootstrap time), and have to include a system like static initialization.

Rather than proliferate dozens of projects that pin people to statics, I would suggest putting better advice here. There's a lot of instrumentation manufacturing going on which don't seem to gel with best practices. I suspect if people knew better, they would make static lookup as a last resort or opt-in vs the way to configure tracing.

Make abstract classes public

Just wondering if there is a particular reason why the abstract classes are not public? If not, I'll submit a PR.

Provide mechanism for unwrapping spans

In openzipkin-contrib/brave-opentracing#39, I oversimplified my solution. My span was actually an AutoReleasingManagedSpan. I had to write additional code to unwrap it, as follows:

    public static Span unwrapSpan(Span span) {
        while (true) {
            if (span instanceof ManagedSpan) {
                ManagedSpan managedSpan = (ManagedSpan) span;
                span = managedSpan.getSpan();
                continue;
            }
            return span;
        }
    }

A solution could go in ot-spanmanager, but that seems like it's going to become part of ot-java soonish and more changes would probably delay that, so I'm posting it here with the expectation that a solution would wait until #115 is merged.

The solution wouldn't necessarily need to be a static utility method. With SpanManager baked into ot-java, Spans could just have an unwrap method that recursively finds the innermost wrapped span. @devinsba has something similar he wrote: https://gitter.im/openzipkin/zipkin?at=5903978bd32c6f2f094dfd75

Clarify inject/extract exceptions

The most common outcome of inject/extract methods that instrumentation needs to deal with are

  1. success
  2. unsupported format
  3. empty carrier (extract only)
  4. other exceptions (including invalid carrier type, corrupted carrier, etc.)

In Python API we specified exactly how tracer must react to these, specifically by throwing known exception for (2) and returning None for (3). The case (4) is usually a result of misconfiguration, so the exact exception is not that important.

I would like to have a well-defined (unchecked) exception type for (2), and specify that null must be returned for (3).

Side note: inject() documentation refers to carrier types instead of formats: @param carrier the carrier for the SpanContext state. All Tracer.inject() implementations must support io.opentracing.propagation.TextMap and java.nio.ByteBuffer.

@bensigelman @michaelsembwever

Add possibility to set the operation name after the span has been created

I'm currently evaluating to use opentracing/jaeger as the foundation of stagemonitor and this is one of the things I've stumbled upon:

As of now, the operation name has to be provided in order to create a span:

Span http = tracer.buildSpan("HandleHTTPRequest")

The problem is that you often don't know the operation name when a HTTP request has just arrived. Setting the operation name to the URL may be sufficient in some cases but as soon as you have url schemes like /item/{itemNumber} you probably want to group them together.

Stagemonitor instruments Spring MVC and JAX-RS to derive the operation name from the controller method. For example

@RequestMapping("/item/{itemNumber})
public void showItemDetail(@PathVaiable String itemNumber) {...}

Results in an operation name like Show Item Details or ItemControler#showItemDetails. Users also may choose to set the operation name at any time in stagemonitor. Note that the Span does not start at the controller method but as soon as the HTTP request hits the topmost servlet Filter.

So I think it would be a useful addition to be able to set the operation name after the start of the span.

How to use brave-opentracing API to let Zipkin to display Server Receive and Server Send?

In my project,I need to use (opentacing-api+brave-opentracing+Zipkin) to tracing.
I use brave-opentracing API start(),finish() to create Span and post ,But In Zipkin only to display Client Send and Client Receive datas ,do not display Server Receive and Server Send.

My questions is How to use brave-opentracing API to let Zipkin to display Server Receive and Server Send?

Demo, There are two micro services,ServiceA and ServiceB
ServiceA remote procedure call ServiceB,via brower to call ServiceA and ServiceA will remote call serviceB,and ServiceA、ServiceB span finish();
But in zipkin,only to display Client Send and Client Receive,
In this demo, How to display Server Receive and Server Send by using brave-opentracing API ?

In ServiceA code

@RequestMapping("ServiceA")
	public String serviceA() throws InterruptedException, IOException
	{
		//BraveTracer  
		//root span
 		Span span0 = tracer.buildSpan("ServiceA").start();
		Map<String, String> map = new HashMap<String, String>();
		tracer.inject(span0.context(), Format.Builtin.HTTP_HEADERS, new TextMapInjectAdapter(map));
 		//build HttpHeader
		HttpHeaders header = new HttpHeaders();
		header.setAll(map);
		//remote invoke
 		String rs = restTemplate.postForEntity("http://localhost:9393/ServiceB", new HttpEntity(header), String.class)
				.getBody();
 		//post
 		span0.finish();
 		return "";
	}

In ServiceB code


	@RequestMapping("ServiceB")
	public String serviceB() throws InterruptedException, IOException
	{
 
		Enumeration<String> e = request.getHeaderNames();
		Map<String, String> spanMap = new HashMap<String, String>();
 
		while (e.hasMoreElements())
		{
			// add the names of the request headers into the spanMap  
			String key = e.nextElement();
			String value = request.getHeader(key);
			spanMap.put(key, value);
 		}
		Span span1 = tracer.buildSpan("ServiceB")
				.asChildOf(tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapExtractAdapter(spanMap))).start();
		
		
		//demo doSomethings
		Thread.sleep(3000);

		//ServiceB post
 		span1.finish();

		return "test";
	} 

Why finished span still active?

I create span, activate it and finish.
But after that tracer shows me that there is active span.
Is it correct?

    MockTracer mockTracer = new MockTracer(new ThreadLocalActiveSpanSource(),
        MockTracer.Propagator.TEXT_MAP);
    Span span = mockTracer.buildSpan("test").startManual();
    mockTracer.makeActive(span);
    span.finish(); // I finish it !!!

    ActiveSpan activeSpan = mockTracer.activeSpan();
    System.out.println(activeSpan == null); // It prints false

How to access Span id (trace id) ?

Hi,

My Use Case, I would like to have read access to the Span id (SpanContext id) to be able to use it in log and in error reported (to end user,...).

Is it possible ?

  • if yes what is the way ?
  • if no, why ?

Thanks.

Propagate mock baggage items

The inject and extract methods in MockTracer.Propagator.TEXT_MAP both completely ignore the baggage items. I suggest simply including them in the same carrier Map as span and trace ids. This does mean that the keys spanid and traceid become reserved keys for baggage items, at least in the mock implementation with current values of SPAN_ID_KEY and TRACE_ID_KEY. They will work normally within a span, but any baggage items with either of those keys will not propagate.

Reorganize modules

I propose the following layout

  • opentracing-api module remains as is
  • opentracing-mock module remains as is
  • opentracing-impl is renamed to opentracing-noop, and propagation package removed from it, or moved to -impl-java8 as that's the only thing that uses it
  • -impl-java8 is removed from this repo completely and migrated to basictracer-java repo, where it can give rise to the actual reference implementation, not just an SPI

Move abstract tracer to contrib

This comes over and over again (most recently here), I think we should address this. The reasons for moving abstract tracer out of opentracing-java are:

  1. it is not used by many (most?) existing production tracers
  2. it does not necessarily reflect how the tracers should be implemented
  3. it has some unexpected, but hard to remove design choices (#55)
  4. it often confuses newcomers being in the official OT Java repo
  5. it's not clear if there are active maintainers of opentracing-java who also want to maintain the abstract tracer (and they can still do it in contrib)
  6. abstract tracer is not needed for development of OT API itself, because implementation questions can be tried out in Noop and Mock tracers, both of which do not extend the abstract tracer.

MockSpan add toString method

Hello,

I'm using MockSpan for demo/debug purposes. It would simplify my life if MockSpan could be easily converted to string (with all tags, logs..), therefore I would like to add toString method to MockSpan class.

What do you think? @bensigelman

SpanBuilder should initialize the start time when start() is called

I'm creating SpanBuilder instances and initializing them with a parent span as well as basic tags. These builders are then used for all child span initialization. However when a second span is created using the same builder the start time of the 2nd span == start time of the 1st. This is unexpected. When you call .start() without calling .withStartTime(micros) then the start time is initialized to the relative nowish value. So I expected that every time .start() is called it generates a new span with tag data and parent span initialized.

I'm also unclear whether the starttime of a span originates from System.nanoTime() or from System.currentTimeMillis() * 1_000L.

Make obvious that this is BYO(in-process propagation)

in general, it might be nice to have a layer diagram of OT, since a lot of tracing systems (in java at least) do in-process propagation. That way it could be more easy to tell what's left to do.
ex I had a late ah-hah (or rather I forgot and learned again) when saw something thread-localling a span. @mansu also had questions about in-process propagation.

The docs are actually quite specific, just it is not something you'd assume (in java).. possibly it could be just local to java (the assumption of implicit propagation), and moved to its README.

ex if you look at most java tracers, they highlight implicit propagation and how to achieve it, as that's needed to be understood before you get to a point of sending it out of process (inject/extract).

inject & join coupling

I'm trying to understand why inject and join use the same carrier type.

For example, if the RPC is over HTTP, the carrier for inject can be any HTTP client HeaderGroup.

However the carrier for Join on the server side can be of a different type, say the Enumeration headerNames in the case of pulling the headers from HttpServletRequest.

If the point of Tracer is to define a tracer per RPC technology (ie. HttpTracer), would it make more sense to provide the capability to use two use different types if needed across join and inject?

Maybe I'm missing the point of the Tracer.

Setup publishing

Interfaces aren't going to be useful for most people unless they can import the jars that contain them. This is a pretty important step, and will require some setup, likely a bintray org, sonatype issue (for maven central), etc.

Until that's in, people need to either build locally or copy/paste things into their project.

Shouldn't Span implement AutoCloseable not Closeable?

AFAIK the Closeable interface was intended for releasing resources related to IO operations...which is why it throws IOException.

Whereas the AutoCloseable interface...was intended for releasing general resources.

This means when we want to...

try(Span span = getSpan()){
    // do stuff
}

We have to handle an IOException....which seems odd...since we are only closing a Span.

Since your POM defines version <main.java.version>1.7</main.java.version>, I'm guessing you didnt use Closeable to maintain backwards compat with Java 6

Should there be such a thing as a `null` activeSpan()? Or SpanContext?

Now that #115 is in, we need to contend with the scenario where ActiveSpanSource.activeSpan() / Tracer.activeSpan() returns null.

As I see it, there are three options.

  1. Do nothing. Callers check for null and get on with their lives.
  2. Specify that implementations use some sort of NoopSpan, but do not specify which
  3. Specify that implementations use a particular instance of a particular object... maybe NoSpan.INSTANCE instead of NoopSpan.INSTANCE. I.e., the caller could do equality testing and be guaranteed to check for the "no-span Span" without adding a new isReal() sort of method to the Span interface. Of course the various tagging methods would do nothing here.

There is the related case of Tracer.extract() when there's, well, nothing to extract. Should it return null, an unspecified non-null SpanContext, or a particular SpanContext instance to represent the case of being absent?

See also https://github.com/opentracing/opentracing-java/pull/115/files/bf4606c596955c7e353cfa92bba372d5ecd2e206#r114108185 and https://github.com/opentracing/opentracing-java/pull/115/files/bf4606c596955c7e353cfa92bba372d5ecd2e206#r109821172 where this came up. And also #111 (comment) .

[Discuss] Tracer is just interface, is it enough? I think it make users difficult to switch implements.

@bensigelman @yurishkuro @michaelsembwever
As OpenTracing specification says, one of OpenTracing specification goals is to let application developers to build trace in their application from beginning, and to choose implements later.
I review the api module recently. I think the definition of interface Tracer may cause users difficult to switch implements.

First, if I am a application developers, in order to use OpenTracing-api, I must new TracerImp(). At this time, I must choose a implements to make sure the "new" can be done. This don't match the specification obviously.

Other way to do this, I must import opentracing-impl or opentracing-noop module, and new NoopTracer() instead. It seems to work. But I am define a tracer many times, when I develop distributed system. When I switch implement to a real tracer, I must change all codes refer to this new operation. It seems not so cool.

I recommend to change add a TracerFactory or TracerResolver, to choose a tracer Implment base on some tech. Such as spi, System.getProperty() (use -D main param). TracerFactory or TracerResolver will return the noop-implements in opentracing-noop module.
Or TracerFactory or TracerResolver provide register operation to let implements to do

What's your opinions about this?
If you agree with me, I will send a pull request to provide this api.

Looking forward your replies.

API break from 0.22 to 0.30

When trying out current camel-opentracing component against a tracer updated to use 0.30.0.RC3, got this:

java.lang.NoSuchMethodError:
io.opentracing.Span.setTag(Ljava/lang/String;Ljava/lang/String;)Lio/opentracing/Span;
at org.apache.camel.opentracing.decorators.AbstractSpanDecorator.pre(AbstractSpanDecorator.java:72) ~[camel-opentracing-2.20.0-SNAPSHOT.jar:2.20.0-SNAPSHOT]

The new split between Span and ActiveSpan, seems a cleaner design, so not suggesting we should revert the change - just making a note that this causes issues between these two versions.

Java 6 compatibility

Hi,

for the background: I'm related to inspectIT (https://github.com/inspectIT/inspectIT) and we are currently integrating a full remoting capability into our tool. Recently, we found your library and we really want to use it as our base. The problem we have currently is that you define Java 7 as a minimum requirement whereas we do need to support Java 6 applications (as many applications in production still use this version).

The only thing we found in your codebase that really needs Java 7 seems to be the usage of the AutoCloseable interface (https://github.com/opentracing/opentracing-java/blob/master/opentracing-api/src/main/java/io/opentracing/Span.java#L23) .

Is there a possibility that you can create a Java 6 compatibility version of your API? Either by removing the dependency on the AutoCloseable interface or maybe through another version somehow? As mentioned, we really want to use your library, it is exactly what we need!

Thanks,
Patrice

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.