diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java index eedca044..5322c09b 100644 --- a/opentracing-api/src/main/java/io/opentracing/Tracer.java +++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java @@ -13,13 +13,15 @@ */ package io.opentracing; +import java.io.Closeable; + import io.opentracing.propagation.Format; import io.opentracing.tag.Tag; /** * Tracer is a simple, thin interface for Span creation and propagation across arbitrary transports. */ -public interface Tracer { +public interface Tracer extends Closeable { /** * @return the current {@link ScopeManager}, which may be a noop but may not be null. @@ -117,6 +119,19 @@ public interface Tracer { */ SpanContext extract(Format format, C carrier); + /** + * Closes the Tracer, and tries to flush the in-memory collection to the configured persistance store. + * + *

+ * The close method should be considered idempotent; closing an already closed Tracer should not raise an error. + * Spans that are created or finished after a Tracer has been closed may or may not be flushed. + * Calling the close method should be considered a synchronous operation. Observe this call may block for + * a relatively long period of time, depending on the internal shutdown. + *

+ * For stateless tracers, this can be a no-op. + */ + @Override + void close(); interface SpanBuilder { 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 4ce9cfe5..92ae9608 100644 --- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java +++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java @@ -53,6 +53,7 @@ public class MockTracer implements Tracer { private final List finishedSpans = new ArrayList<>(); private final Propagator propagator; private final ScopeManager scopeManager; + private boolean isClosed; public MockTracer() { this(new ThreadLocalScopeManager(), Propagator.TEXT_MAP); @@ -280,7 +281,16 @@ public Scope activateSpan(Span span) { return this.scopeManager.activate(span); } + @Override + public synchronized void close() { + this.isClosed = true; + this.finishedSpans.clear(); + } + synchronized void appendFinishedSpan(MockSpan mockSpan) { + if (isClosed) + return; + this.finishedSpans.add(mockSpan); this.onSpanFinished(mockSpan); } 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 11e7e5f5..5f521f53 100644 --- a/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java +++ b/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java @@ -399,4 +399,16 @@ public void testChildOfWithNullParentDoesNotThrowException() { Span span = tracer.buildSpan("foo").asChildOf(parent).start(); span.finish(); } + + @Test + public void testClose() { + MockTracer mockTracer = new MockTracer(); + mockTracer.buildSpan("foo").start().finish(); + + mockTracer.close(); + assertEquals(0, mockTracer.finishedSpans().size()); + + mockTracer.buildSpan("foo").start().finish(); + assertEquals(0, mockTracer.finishedSpans().size()); + } } diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java index a03c50eb..dc9d95fe 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java @@ -50,6 +50,9 @@ public void inject(SpanContext spanContext, Format format, C carrier) {} @Override public SpanContext extract(Format format, C carrier) { return NoopSpanContextImpl.INSTANCE; } + @Override + public void close() {} + @Override public String toString() { return NoopTracer.class.getSimpleName(); } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java index e4dea6a7..b9ebb7b7 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java +++ b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java @@ -214,6 +214,11 @@ public Scope activateSpan(Span span) { return tracer.activateSpan(span); } + @Override + public void close() { + tracer.close(); + } + @Override public String toString() { return GlobalTracer.class.getSimpleName() + '{' + tracer + '}'; diff --git a/opentracing-util/src/test/java/io/opentracing/util/GlobalTracerTest.java b/opentracing-util/src/test/java/io/opentracing/util/GlobalTracerTest.java index 85609ea4..e7bc89f3 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/GlobalTracerTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/GlobalTracerTest.java @@ -45,6 +45,7 @@ import static org.junit.Assert.fail; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -225,6 +226,16 @@ public void testDelegation_extract() { verifyNoMoreInteractions(mockTracer, mockFormat, mockCarrier); } + @Test + public void testDelegation_close() { + Tracer mockTracer = mock(Tracer.class); + GlobalTracer.register(mockTracer); + GlobalTracer.get().close(); + + verify(mockTracer, times(1)).close(); + verifyNoMoreInteractions(mockTracer); + } + @Test public void concurrencyTest() throws InterruptedException, ExecutionException { final int threadCount = 10;