.");
+ this.runnable = runnable;
+ this.manager = manager;
+ this.snapshot = manager.snapshot(span);
+ }
+
+ @Override
+ public void run() {
+ final Span span = manager.activate(this.snapshot);
+ try {
+ runnable.run();
+ } finally {
+ manager.deactivate(this.snapshot);
+ }
+ }
+}
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/AbstractSpan.java b/opentracing-impl/src/main/java/io/opentracing/impl/AbstractSpan.java
index 4305684d..b3f08120 100644
--- a/opentracing-impl/src/main/java/io/opentracing/impl/AbstractSpan.java
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/AbstractSpan.java
@@ -55,6 +55,11 @@ public void finish() {
duration = Duration.between(start, Instant.now());
}
+ @Override
+ public boolean isFinished() {
+ return null != duration;
+ }
+
@Override
public void finish(long finishMicros) {
long finishEpochSeconds = TimeUnit.MICROSECONDS.toSeconds(finishMicros);
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/AbstractTracer.java b/opentracing-impl/src/main/java/io/opentracing/impl/AbstractTracer.java
index 9b0c8169..a3dca44e 100644
--- a/opentracing-impl/src/main/java/io/opentracing/impl/AbstractTracer.java
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/AbstractTracer.java
@@ -13,6 +13,8 @@
*/
package io.opentracing.impl;
+import io.opentracing.ActiveSpanManager;
+import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.propagation.Extractor;
@@ -28,6 +30,7 @@ abstract class AbstractTracer implements Tracer {
static final boolean BAGGAGE_ENABLED = !Boolean.getBoolean("opentracing.propagation.dropBaggage");
private final PropagationRegistry registry = new PropagationRegistry();
+ private ActiveSpanManager manager;
protected AbstractTracer() {
registry.register(Format.Builtin.TEXT_MAP, new TextMapInjectorImpl(this));
@@ -36,6 +39,16 @@ protected AbstractTracer() {
abstract AbstractSpanBuilder createSpanBuilder(String operationName);
+ @Override
+ public void setActiveSpanManager(ActiveSpanManager manager) {
+ this.manager = manager;
+ }
+
+ @Override
+ public ActiveSpanManager activeSpanManager() {
+ return this.manager;
+ }
+
@Override
public SpanBuilder buildSpan(String operationName){
return createSpanBuilder(operationName);
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/GlobalTracer.java b/opentracing-impl/src/main/java/io/opentracing/impl/GlobalTracer.java
new file mode 100644
index 00000000..6d7ac692
--- /dev/null
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/GlobalTracer.java
@@ -0,0 +1,160 @@
+package io.opentracing.impl;
+
+/**
+ * Copyright 2017 The OpenTracing Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import io.opentracing.*;
+import io.opentracing.propagation.Format;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Forwards all methods to another tracer that can be configured in one of two ways:
+ *
+ * - Explicitly, calling {@link #register(Tracer)} with a configured tracer, or:
+ * - Automatically using the Java {@link ServiceLoader} SPI mechanism to load a {@link Tracer} from the classpath.
+ *
+ *
+ * When the tracer is needed it is lazily looked up using the following rules:
+ *
+ * - The last-{@link #register(Tracer) registered} tracer always takes precedence.
+ * - If no tracer was registered, one is looked up from the {@link ServiceLoader}.
+ * The {@linkplain GlobalTracer} will not attempt to choose between implementations:
+ * - If no single tracer service is found, the {@link io.opentracing.NoopTracer NoopTracer} will be used.
+ *
+ */
+public final class GlobalTracer implements Tracer {
+ private static final Logger LOGGER = Logger.getLogger(GlobalTracer.class.getName());
+
+ /**
+ * Singleton instance.
+ *
+ * Since we cannot prevent people using {@linkplain #get() GlobalTracer.get()} as a constant,
+ * this guarantees that references obtained before, during or after initialization
+ * all behave as if obtained after initialization once properly initialized.
+ * As a minor additional benefit it makes it harder to circumvent the {@link Tracer} API.
+ */
+ private static final GlobalTracer INSTANCE = new GlobalTracer();
+
+ /**
+ * The resolved {@link Tracer} to delegate to.
+ *
+ * This can be either an {@link #register(Tracer) explicitly registered} or
+ * the {@link #loadSingleSpiImplementation() automatically resolved} Tracer
+ * (or null during initialization).
+ */
+ private final AtomicReference globalTracer = new AtomicReference();
+
+ private GlobalTracer() {
+ }
+
+ private Tracer lazyTracer() {
+ Tracer tracer = globalTracer.get();
+ if (tracer == null) {
+ final Tracer resolved = loadSingleSpiImplementation();
+ while (tracer == null && resolved != null) { // handle rare race condition
+ globalTracer.compareAndSet(null, resolved);
+ tracer = globalTracer.get();
+ }
+ LOGGER.log(Level.INFO, "Using GlobalTracer: {0}.", tracer);
+ }
+ return tracer;
+ }
+
+ /**
+ * Returns the constant {@linkplain GlobalTracer}.
+ *
+ * All methods are forwarded to the currently configured tracer.
+ * Until a tracer is {@link #register(Tracer) explicitly configured},
+ * one is looked up from the {@link ServiceLoader},
+ * falling back to the {@link io.opentracing.NoopTracer NoopTracer}.
+ * A tracer can be re-configured at any time.
+ * For example, the tracer used to extract a span may be different than the one that injects it.
+ *
+ * @return The global tracer constant.
+ * @see #register(Tracer)
+ */
+ public static Tracer get() {
+ return INSTANCE;
+ }
+
+ /**
+ * Explicitly configures a {@link Tracer} to back the behaviour of the {@link #get() global tracer}.
+ *
+ * The previous global tracer is returned so it can be restored later if necessary.
+ *
+ * @param tracer Tracer to use as global tracer.
+ * @return The previous global tracer or null if there was none.
+ */
+ public static Tracer register(final Tracer tracer) {
+ if (tracer instanceof GlobalTracer) {
+ LOGGER.log(Level.FINE, "Attempted to register the GlobalTracer as delegate of itself.");
+ return INSTANCE.globalTracer.get(); // no-op, return 'previous' tracer.
+ }
+ Tracer previous = INSTANCE.globalTracer.getAndSet(tracer);
+ LOGGER.log(Level.INFO, "Registered GlobalTracer {0} (previously {1}).", new Object[]{tracer, previous});
+ return previous;
+ }
+
+ @Override
+ public SpanBuilder buildSpan(String operationName) {
+ return lazyTracer().buildSpan(operationName);
+ }
+
+ @Override
+ public void setActiveSpanManager(ActiveSpanManager mgr) {
+ lazyTracer().setActiveSpanManager(mgr);
+ }
+
+ @Override
+ public ActiveSpanManager activeSpanManager() {
+ return lazyTracer().activeSpanManager();
+ }
+
+ @Override
+ public void inject(SpanContext spanContext, Format format, C carrier) {
+ lazyTracer().inject(spanContext, format, carrier);
+ }
+
+ @Override
+ public SpanContext extract(Format format, C carrier) {
+ return lazyTracer().extract(format, carrier);
+ }
+
+ /**
+ * Loads a single service implementation from {@link ServiceLoader}.
+ *
+ * @return The single service or a NoopTracer.
+ */
+ private static Tracer loadSingleSpiImplementation() {
+ // Use the ServiceLoader to find the declared Tracer implementation.
+ Iterator spiImplementations =
+ ServiceLoader.load(Tracer.class, Tracer.class.getClassLoader()).iterator();
+ if (spiImplementations.hasNext()) {
+ Tracer foundImplementation = spiImplementations.next();
+ if (!spiImplementations.hasNext()) {
+ return foundImplementation;
+ }
+ LOGGER.log(Level.WARNING, "More than one Tracer service found. " +
+ "Falling back to NoopTracer implementation.");
+ }
+ return NoopTracerFactory.create();
+ }
+
+}
+
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpan.java b/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpan.java
index de0f832b..be8b5d7d 100644
--- a/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpan.java
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpan.java
@@ -13,8 +13,7 @@
*/
package io.opentracing.impl;
-import io.opentracing.NoopSpanContext;
-import io.opentracing.Span;
+import io.opentracing.*;
final class NoopSpan extends AbstractSpan implements io.opentracing.NoopSpan, NoopSpanContext {
@@ -28,6 +27,11 @@ public NoopSpan(String operationName) {
public void finish() {
}
+ @Override
+ public boolean isFinished() {
+ return true;
+ }
+
@Override
public void finish(long finishMicros) {
}
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpanBuilder.java b/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpanBuilder.java
index 08976fc6..77b0e8a2 100644
--- a/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpanBuilder.java
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/NoopSpanBuilder.java
@@ -15,6 +15,8 @@
import io.opentracing.NoopSpanContext;
+import java.util.Map;
+
final class NoopSpanBuilder extends AbstractSpanBuilder implements io.opentracing.NoopSpanBuilder, NoopSpanContext {
static final NoopSpanBuilder INSTANCE = new NoopSpanBuilder("noop");
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/NoopTracer.java b/opentracing-impl/src/main/java/io/opentracing/impl/NoopTracer.java
index e990d019..859bdf24 100644
--- a/opentracing-impl/src/main/java/io/opentracing/impl/NoopTracer.java
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/NoopTracer.java
@@ -13,7 +13,7 @@
*/
package io.opentracing.impl;
-import io.opentracing.SpanContext;
+import io.opentracing.*;
import io.opentracing.propagation.Format;
import java.util.Collections;
import java.util.Map;
@@ -22,6 +22,12 @@ final class NoopTracer extends AbstractTracer implements io.opentracing.NoopTrac
private static final NoopTracer INSTANCE = new NoopTracer();
+ @Override
+ public void setActiveSpanManager(ActiveSpanManager mgr) {}
+
+ @Override
+ public ActiveSpanManager activeSpanManager() { return null; }
+
@Override
public void inject(SpanContext spanContext, Format format, C carrier) {}
@@ -40,5 +46,4 @@ Map getTraceState(SpanContext spanContext) {
return Collections.emptyMap();
}
-
}
diff --git a/opentracing-impl/src/main/java/io/opentracing/impl/TextMapExtractorImpl.java b/opentracing-impl/src/main/java/io/opentracing/impl/TextMapExtractorImpl.java
index 4e80ab87..53b29c1a 100644
--- a/opentracing-impl/src/main/java/io/opentracing/impl/TextMapExtractorImpl.java
+++ b/opentracing-impl/src/main/java/io/opentracing/impl/TextMapExtractorImpl.java
@@ -41,4 +41,4 @@ public Tracer.SpanBuilder extract(TextMap carrier) {
return builder;
}
-}
\ No newline at end of file
+}
diff --git a/opentracing-impl/src/test/java/io/opentracing/impl/AbstractTracerTest.java b/opentracing-impl/src/test/java/io/opentracing/impl/AbstractTracerTest.java
index c45a2fac..bd0aa7f3 100644
--- a/opentracing-impl/src/test/java/io/opentracing/impl/AbstractTracerTest.java
+++ b/opentracing-impl/src/test/java/io/opentracing/impl/AbstractTracerTest.java
@@ -133,6 +133,7 @@ public void propagatesBaggageFromSpanContext() {
final class TestTracerImpl extends AbstractTracer {
static final String OPERATION_NAME = "operation-name";
+ Span activeSpan;
@Override
public AbstractSpanBuilder createSpanBuilder(String operationName) {
diff --git a/opentracing-impl/src/test/java/io/opentracing/impl/TestSpanImpl.java b/opentracing-impl/src/test/java/io/opentracing/impl/TestSpanImpl.java
index 634fbb34..0ef62481 100644
--- a/opentracing-impl/src/test/java/io/opentracing/impl/TestSpanImpl.java
+++ b/opentracing-impl/src/test/java/io/opentracing/impl/TestSpanImpl.java
@@ -20,4 +20,9 @@ public class TestSpanImpl extends AbstractSpan {
TestSpanImpl(String operationName) {
super(operationName);
}
+
+ @Override
+ public boolean isFinished() {
+ return true;
+ }
}
diff --git a/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
index 47a8469b..69eafab9 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
@@ -16,6 +16,7 @@
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
+import io.opentracing.ActiveSpanManager;
import io.opentracing.Span;
import io.opentracing.SpanContext;
@@ -38,6 +39,7 @@ public final class MockSpan implements Span {
private final Map tags;
private final List logEntries = new ArrayList<>();
private String operationName;
+ private ActiveSpanManager.Snapshot snapshot;
public String operationName() {
return this.operationName;
@@ -96,6 +98,9 @@ public void finish() {
@Override
public synchronized void finish(long finishMicros) {
if (!finished) {
+ if (this.mockTracer.activeSpanManager() != null) {
+ this.mockTracer.activeSpanManager().deactivate(this.snapshot);
+ }
this.finishMicros = finishMicros;
this.mockTracer.appendFinishedSpan(this);
this.finished = true;
@@ -104,6 +109,11 @@ public synchronized void finish(long finishMicros) {
}
}
+ @Override
+ public synchronized boolean isFinished() {
+ return finished;
+ }
+
@Override
public void close() {
this.finish();
@@ -253,6 +263,11 @@ public long timestampMicros() {
this.context = new MockContext(parent.traceId, nextId(), parent.baggage);
this.parentId = parent.spanId;
}
+
+ if (tracer.activeSpanManager() != null) {
+ // XXX weird/awkward
+ this.snapshot = tracer.activeSpanManager().snapshot(this);
+ }
}
static long nextId() {
diff --git a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
index 27ca15ff..92f12991 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
@@ -20,10 +20,7 @@
import java.util.List;
import java.util.Map;
-import io.opentracing.References;
-import io.opentracing.Span;
-import io.opentracing.SpanContext;
-import io.opentracing.Tracer;
+import io.opentracing.*;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
@@ -38,6 +35,7 @@
public class MockTracer implements Tracer {
private List finishedSpans = new ArrayList<>();
private final Propagator propagator;
+ private ActiveSpanManager activeSpanManager;
public MockTracer() {
this(Propagator.PRINTER);
@@ -146,7 +144,24 @@ public MockSpan.MockContext extract(Format format, C carrier) {
@Override
public Tracer.SpanBuilder buildSpan(String operationName) {
- return new SpanBuilder(operationName);
+ SpanBuilder sb = new SpanBuilder(operationName);
+ if (this.activeSpanManager != null) {
+ Span active = this.activeSpanManager.active();
+ if (active != null) {
+ sb.asChildOf(active.context());
+ }
+ }
+ return sb;
+ }
+
+ @Override
+ public void setActiveSpanManager(ActiveSpanManager mgr) {
+ this.activeSpanManager = mgr;
+ }
+
+ @Override
+ public ActiveSpanManager activeSpanManager() {
+ return activeSpanManager;
}
@Override
@@ -221,7 +236,12 @@ public Span start() {
if (this.startMicros == 0) {
this.startMicros = MockSpan.nowMicros();
}
- return new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags, this.firstParent);
+ Span rval = new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags, this.firstParent);
+ if (MockTracer.this.activeSpanManager != null) {
+ MockTracer.this.activeSpanManager.activate(
+ MockTracer.this.activeSpanManager.snapshot(rval));
+ }
+ return rval;
}
@Override
diff --git a/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java b/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java
index c24959c6..e956306a 100644
--- a/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java
+++ b/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
+import io.opentracing.MDCActiveSpanManager;
import org.junit.Assert;
import org.junit.Test;
@@ -30,7 +31,35 @@
import io.opentracing.propagation.TextMapExtractAdapter;
import io.opentracing.propagation.TextMapInjectAdapter;
+import org.slf4j.Logger;
+import org.slf4j.MDC;
+
public class MockTracerTest {
+ @Test
+ public void testMDCHack() {
+ org.apache.log4j.BasicConfigurator.configure();
+ Logger logger = org.slf4j.LoggerFactory.getLogger("hack");
+ MDC.put("key1", "val1");
+ Map ctxMap = MDC.getCopyOfContextMap();
+ MDC.put("key2", "val2");
+ MDC.setContextMap(ctxMap);
+ logger.info("testing: {}", MDC.getCopyOfContextMap().toString());
+
+ MockTracer tracer = new MockTracer();
+ tracer.setActiveSpanManager(new MDCActiveSpanManager());
+
+ Span par = tracer.buildSpan("parent").start();
+ Span childA = tracer.buildSpan("childA").start();
+ childA.finish();
+ par.finish();
+
+ List finishedSpans = tracer.finishedSpans();
+
+ for (MockSpan span : finishedSpans) {
+ logger.info("finished Span. {} :: {} ({})", span.context().traceId(), span.context().spanId(), span.parentId());
+ }
+ }
+
@Test
public void testRootSpan() {
// Create and finish a root Span.
diff --git a/opentracing-noop/src/main/java/io/opentracing/NoopSpan.java b/opentracing-noop/src/main/java/io/opentracing/NoopSpan.java
index 8e971804..5fe3804d 100644
--- a/opentracing-noop/src/main/java/io/opentracing/NoopSpan.java
+++ b/opentracing-noop/src/main/java/io/opentracing/NoopSpan.java
@@ -21,7 +21,6 @@ public interface NoopSpan extends Span {
final class NoopSpanImpl implements NoopSpan {
-
@Override
public SpanContext context() { return NoopSpanContextImpl.INSTANCE; }
@@ -31,6 +30,9 @@ public void finish() {}
@Override
public void finish(long finishMicros) {}
+ @Override
+ public boolean isFinished() { return true; }
+
@Override
public void close() { finish(); }
diff --git a/opentracing-noop/src/main/java/io/opentracing/NoopSpanSnapshot.java b/opentracing-noop/src/main/java/io/opentracing/NoopSpanSnapshot.java
new file mode 100644
index 00000000..a484a72d
--- /dev/null
+++ b/opentracing-noop/src/main/java/io/opentracing/NoopSpanSnapshot.java
@@ -0,0 +1,9 @@
+package io.opentracing;
+
+public interface NoopSpanSnapshot {
+ static final NoopSpanSnapshotImpl INSTANCE = new NoopSpanSnapshotImpl();
+}
+
+final class NoopSpanSnapshotImpl implements NoopSpanSnapshot {
+
+}
\ No newline at end of file
diff --git a/opentracing-noop/src/main/java/io/opentracing/NoopTracer.java b/opentracing-noop/src/main/java/io/opentracing/NoopTracer.java
index 40d17a9a..dbe02a61 100644
--- a/opentracing-noop/src/main/java/io/opentracing/NoopTracer.java
+++ b/opentracing-noop/src/main/java/io/opentracing/NoopTracer.java
@@ -24,6 +24,12 @@ final class NoopTracerImpl implements NoopTracer {
@Override
public SpanBuilder buildSpan(String operationName) { return NoopSpanBuilderImpl.INSTANCE; }
+ @Override
+ public void setActiveSpanManager(ActiveSpanManager mgr) {}
+
+ @Override
+ public ActiveSpanManager activeSpanManager() { return null; }
+
@Override
public void inject(SpanContext spanContext, Format format, C carrier) {}
diff --git a/pom.xml b/pom.xml
index 142bcd68..973f1021 100644
--- a/pom.xml
+++ b/pom.xml
@@ -238,7 +238,7 @@
true
-
+
maven-release-plugin
${maven-release-plugin.version}