From 36a57df3c7db991436776a79e41d46f537fe8b9b Mon Sep 17 00:00:00 2001 From: Debashismitra01 Date: Tue, 7 Jul 2026 03:00:24 +0530 Subject: [PATCH 1/4] Add shutdown lifecycle method to Sampler --- .../io/opentelemetry/sdk/trace/samplers/Sampler.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/samplers/Sampler.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/samplers/Sampler.java index 73edba3b6f1..0b987d0ac53 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/samplers/Sampler.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/samplers/Sampler.java @@ -11,6 +11,7 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.TraceId; import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.LinkData; import java.util.List; import javax.annotation.concurrent.ThreadSafe; @@ -119,4 +120,15 @@ SamplingResult shouldSample( * @return the description of this {@code Sampler}. */ String getDescription(); + + /** + * Shuts down the sampler, releasing any resources held by it. + * + *

This method is called when the {@code SdkTracerProvider} is shut down. + * + * @return a {@link CompletableResultCode} which is completed when shutdown is finished. + */ + default CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } } From f5f8934324af040bde8f3ca72c810c530a997826 Mon Sep 17 00:00:00 2001 From: Debashismitra01 Date: Tue, 7 Jul 2026 03:00:50 +0530 Subject: [PATCH 2/4] Shutdown sampler from SdkTracerProvider --- .../sdk/trace/SdkTracerProvider.java | 15 +++++++++------ .../sdk/trace/SdkTracerProviderTest.java | 13 +++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java index 7a555ea128b..6ef5f40000a 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java @@ -135,25 +135,28 @@ void setTracerConfigurator(ScopeConfigurator tracerConfigurator) { /** * Attempts to stop all the activity for {@link Tracer}s created by this provider. Calls {@link - * SpanProcessor#shutdown()} for all registered {@link SpanProcessor}s. + * SpanProcessor#shutdown()} for all registered {@link SpanProcessor}s and {@link + * Sampler#shutdown()} for the configured {@link Sampler}. * - *

The returned {@link CompletableResultCode} will be completed when all the Spans are - * processed. + *

The returned {@link CompletableResultCode} will be completed when shutdown processing is + * finished. * *

After this is called, newly created {@code Span}s will be no-ops. * *

After this is called, further attempts at re-using this instance will result in undefined * behavior. It should be considered a terminal operation for the SDK. * - * @return a {@link CompletableResultCode} which is completed when all the span processors have - * been shut down. + * @return a {@link CompletableResultCode} which is completed when all span processors and the + * sampler have been shut down. */ public CompletableResultCode shutdown() { if (sharedState.hasBeenShutdown()) { logger.log(Level.INFO, "Calling shutdown() multiple times."); return CompletableResultCode.ofSuccess(); } - return sharedState.shutdown(); + + return CompletableResultCode.ofAll( + List.of(sharedState.shutdown(), sharedState.getSampler().shutdown())); } /** diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java index b007fe06b22..4ba416c4f88 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java @@ -237,6 +237,19 @@ void shutdown() { Mockito.verify(spanProcessor, Mockito.times(1)).shutdown(); } + @Test + void shutdown_ShutsDownSampler() { + Sampler sampler = mock(Sampler.class); + + when(sampler.shutdown()).thenReturn(CompletableResultCode.ofSuccess()); + + SdkTracerProvider tracerProvider = SdkTracerProvider.builder().setSampler(sampler).build(); + + tracerProvider.shutdown(); + + verify(sampler).shutdown(); + } + @Test void close() { tracerProvider.close(); From 83b70e5eec7930ec6c8f8704f090314aebc1c499 Mon Sep 17 00:00:00 2001 From: Debashismitra01 Date: Tue, 7 Jul 2026 03:01:19 +0530 Subject: [PATCH 3/4] Shutdown JaegerRemoteSampler resources --- CHANGELOG.md | 3 +++ .../jaeger/sampler/JaegerRemoteSampler.java | 12 +++++++++--- .../sampler/JaegerRemoteSamplerTest.java | 14 ++++++++++++++ .../sdk/trace/SdkTracerProvider.java | 15 ++++++--------- .../sdk/trace/TracerSharedState.java | 19 +++++++++++++++---- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c399e74acfa..b7e234aad3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Add shutdown lifecycle method to `Sampler` to allow resource cleanup for stateful samplers. + ([#8574](https://github.com/open-telemetry/opentelemetry-java/pull/8574)) + ## Version 1.63.0 (2026-06-05) ### API diff --git a/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSampler.java b/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSampler.java index 2e32f566273..e26dcabbd46 100644 --- a/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSampler.java +++ b/sdk-extensions/jaeger-remote-sampler/src/main/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSampler.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.export.GrpcResponse; import io.opentelemetry.sdk.common.export.GrpcSender; import io.opentelemetry.sdk.common.export.GrpcStatusCode; @@ -175,11 +176,16 @@ public static JaegerRemoteSamplerBuilder builder() { return new JaegerRemoteSamplerBuilder(); } + @Override + public CompletableResultCode shutdown() { + pollFuture.cancel(false); + pollExecutor.shutdownNow(); + return grpcSender.shutdown(); + } + @Override @SuppressWarnings("Interruption") public void close() { - pollFuture.cancel(true); - pollExecutor.shutdownNow(); - grpcSender.shutdown().join(10, TimeUnit.SECONDS); + shutdown().join(10, TimeUnit.SECONDS); } } diff --git a/sdk-extensions/jaeger-remote-sampler/src/test/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerTest.java b/sdk-extensions/jaeger-remote-sampler/src/test/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerTest.java index e646a0d4921..969bf04a934 100644 --- a/sdk-extensions/jaeger-remote-sampler/src/test/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerTest.java +++ b/sdk-extensions/jaeger-remote-sampler/src/test/java/io/opentelemetry/sdk/extension/trace/jaeger/sampler/JaegerRemoteSamplerTest.java @@ -160,6 +160,20 @@ void connectionWorks() { } } + @Test + void shutdown() { + JaegerRemoteSampler sampler = + JaegerRemoteSampler.builder() + .setEndpoint(server.httpUri().toString()) + .setPollingInterval(1, TimeUnit.SECONDS) + .setServiceName(SERVICE_NAME) + .build(); + + assertThat(sampler).extracting("grpcSender").isInstanceOf(OkHttpGrpcSender.class); + + assertThat(sampler.shutdown().join(10, TimeUnit.SECONDS).isSuccess()).isTrue(); + } + @Test void tlsConnectionWorks() throws IOException { try (JaegerRemoteSampler sampler = diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java index 6ef5f40000a..7a555ea128b 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java @@ -135,28 +135,25 @@ void setTracerConfigurator(ScopeConfigurator tracerConfigurator) { /** * Attempts to stop all the activity for {@link Tracer}s created by this provider. Calls {@link - * SpanProcessor#shutdown()} for all registered {@link SpanProcessor}s and {@link - * Sampler#shutdown()} for the configured {@link Sampler}. + * SpanProcessor#shutdown()} for all registered {@link SpanProcessor}s. * - *

The returned {@link CompletableResultCode} will be completed when shutdown processing is - * finished. + *

The returned {@link CompletableResultCode} will be completed when all the Spans are + * processed. * *

After this is called, newly created {@code Span}s will be no-ops. * *

After this is called, further attempts at re-using this instance will result in undefined * behavior. It should be considered a terminal operation for the SDK. * - * @return a {@link CompletableResultCode} which is completed when all span processors and the - * sampler have been shut down. + * @return a {@link CompletableResultCode} which is completed when all the span processors have + * been shut down. */ public CompletableResultCode shutdown() { if (sharedState.hasBeenShutdown()) { logger.log(Level.INFO, "Calling shutdown() multiple times."); return CompletableResultCode.ofSuccess(); } - - return CompletableResultCode.ofAll( - List.of(sharedState.shutdown(), sharedState.getSampler().shutdown())); + return sharedState.shutdown(); } /** diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java index 254d78fd254..78c7b48d43a 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java @@ -10,6 +10,7 @@ import io.opentelemetry.sdk.common.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.samplers.Sampler; +import java.util.Arrays; import java.util.List; import java.util.function.Supplier; import javax.annotation.Nullable; @@ -106,18 +107,28 @@ SdkTracerInstrumentation getTracerInstrumentation() { } /** - * Stops tracing, including shutting down processors and set to {@code true} {@link + * Stops tracing, including shutting down processors and sampler and set to {@code true} {@link * #hasBeenShutdown()}. * - * @return a {@link CompletableResultCode} that will be completed when the span processor is shut - * down. + * @return a {@link CompletableResultCode} that will be completed when the span processor and + * sampler are shut down. */ CompletableResultCode shutdown() { synchronized (lock) { if (shutdownResult != null) { return shutdownResult; } - shutdownResult = activeSpanProcessor.shutdown(); + + CompletableResultCode samplerShutdownResult = sampler.shutdown(); + + shutdownResult = + CompletableResultCode.ofAll( + Arrays.asList( + activeSpanProcessor.shutdown(), + samplerShutdownResult == null + ? CompletableResultCode.ofSuccess() + : samplerShutdownResult)); + return shutdownResult; } } From f246d3e2dd8de137547dd376e5f3056ec91a72ab Mon Sep 17 00:00:00 2001 From: Debashismitra01 Date: Tue, 7 Jul 2026 04:46:25 +0530 Subject: [PATCH 4/4] Update API compatibility reports --- .../opentelemetry-sdk-extension-jaeger-remote-sampler.txt | 4 +++- docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt index 4c66caca34c..0ca5622ab3a 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt @@ -1,2 +1,4 @@ Comparing source compatibility of opentelemetry-sdk-extension-jaeger-remote-sampler-1.64.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.63.0.jar -No changes. \ No newline at end of file +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.CompletableResultCode shutdown() diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt index 069a14f1761..29677806989 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt @@ -1,2 +1,4 @@ Comparing source compatibility of opentelemetry-sdk-trace-1.64.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.63.0.jar -No changes. \ No newline at end of file +*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.trace.samplers.Sampler (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.CompletableResultCode shutdown()