diff --git a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java index 2bef978b..9348abfa 100644 --- a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java +++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java @@ -91,6 +91,16 @@ public interface ScopeManager { */ Span activeSpan(); + /** + * Clear the current active state, setting both {@link #active()} and + * {@link #activeSpan()} to null for the current context (usually a thread). + * + *

+ * This method can be called to discard any previously leaked {@link Scope} objects + * that were unintentionally left active. + */ + void clear(); + /** * @deprecated use {@link #activate(Span)} instead. * Set the specified {@link Span} as the active instance for the current diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java index 911ac5a1..726ab835 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java @@ -49,6 +49,10 @@ public Span activeSpan() { return NoopSpan.INSTANCE; } + @Override + public void clear() { + } + static class NoopScopeImpl implements NoopScopeManager.NoopScope { @Override public void close() {} diff --git a/opentracing-util/src/main/java/io/opentracing/util/AutoFinishScopeManager.java b/opentracing-util/src/main/java/io/opentracing/util/AutoFinishScopeManager.java index 0a60deb3..489b4820 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/AutoFinishScopeManager.java +++ b/opentracing-util/src/main/java/io/opentracing/util/AutoFinishScopeManager.java @@ -41,4 +41,11 @@ public Span activeSpan() { AutoFinishScope scope = tlsScope.get(); return scope == null ? null : scope.span(); } + + @Override + public void clear() { + // Set a null value instead of calling remove(), + // to prevent unnecessary allocation of new ThreadLocal-related objects. + tlsScope.set(null); + } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java index 433f4bc0..fe463374 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java @@ -45,4 +45,11 @@ public Span activeSpan() { Scope scope = tlsScope.get(); return scope == null ? null : scope.span(); } + + @Override + public void clear() { + // Set a null value instead of calling remove(), + // to prevent unnecessary allocation of new ThreadLocal-related objects. + tlsScope.set(null); + } } diff --git a/opentracing-util/src/test/java/io/opentracing/util/AutoFinishScopeManagerTest.java b/opentracing-util/src/test/java/io/opentracing/util/AutoFinishScopeManagerTest.java index b78e6ac3..c719c649 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/AutoFinishScopeManagerTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/AutoFinishScopeManagerTest.java @@ -57,4 +57,18 @@ public void activateSpan() throws Exception { Scope missingSpan = source.active(); assertNull(missingSpan); } + + @Test + public void clear() throws Exception { + Span span = mock(Span.class); + + Scope scope = source.activate(span); + assertNotNull(scope); + assertNotNull(source.active()); + assertNotNull(source.activeSpan()); + + source.clear(); + assertNull(source.active()); + assertNull(source.activeSpan()); + } } diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java index 26e88c0f..e84478eb 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java @@ -114,4 +114,18 @@ public void dontFinishSpanNoClose() throws Exception { assertNull(source.active()); assertNull(source.activeSpan()); } + + @Test + public void clear() throws Exception { + Span span = mock(Span.class); + + Scope scope = source.activate(span); + assertNotNull(scope); + assertNotNull(source.active()); + assertNotNull(source.activeSpan()); + + source.clear(); + assertNull(source.active()); + assertNull(source.activeSpan()); + } }