Add shutdown / close to OpenTelemetrySdk (#5100)

* Add shutdown / close to OpenTelemetrySdk

* Shutdown hook closes instead of shutdown
This commit is contained in:
jack-berg 2023-01-10 16:05:21 -06:00 committed by GitHub
parent 50e7a17309
commit 4df4a0ae09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 43 deletions

View File

@ -1,2 +1,5 @@
Comparing source compatibility of against
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.OpenTelemetrySdk (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) void close()
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.CompletableResultCode shutdown()

View File

@ -10,7 +10,6 @@ import eu.rekawek.toxiproxy.ToxiproxyClient;
import eu.rekawek.toxiproxy.model.ToxicDirection;
import eu.rekawek.toxiproxy.model.ToxicList;
import eu.rekawek.toxiproxy.model.toxic.Timeout;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
@ -106,9 +105,7 @@ public class OtlpPipelineStressTest {
private final InMemoryMetricExporter metricExporter = InMemoryMetricExporter.create();
private SdkTracerProvider sdkTracerProvider;
private OpenTelemetry openTelemetry;
private SdkMeterProvider meterProvider;
private OpenTelemetrySdk openTelemetry;
private Proxy collectorProxy;
private ToxiproxyClient toxiproxyClient;
@ -134,8 +131,7 @@ public class OtlpPipelineStressTest {
@AfterEach
void tearDown() throws IOException {
meterProvider.close();
sdkTracerProvider.shutdown();
openTelemetry.close();
toxiproxyClient.reset();
collectorProxy.delete();
@ -188,7 +184,7 @@ public class OtlpPipelineStressTest {
Thread.sleep(10000);
List<MetricData> finishedMetricItems = metricExporter.getFinishedMetricItems();
meterProvider.close();
openTelemetry.getSdkMeterProvider().close();
Thread.sleep(1000);
reportMetrics(finishedMetricItems);
Thread.sleep(10000);
@ -250,7 +246,7 @@ public class OtlpPipelineStressTest {
.build());
// set up the metric exporter and wire it into the SDK and a timed reader.
meterProvider =
SdkMeterProvider meterProvider =
SdkMeterProvider.builder()
.setResource(resource)
.registerMetricReader(
@ -281,7 +277,9 @@ public class OtlpPipelineStressTest {
SdkTracerProvider tracerProvider =
SdkTracerProvider.builder().addSpanProcessor(spanProcessor).build();
openTelemetry =
OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();
sdkTracerProvider = tracerProvider;
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.buildAndRegisterGlobal();
}
}

View File

@ -17,7 +17,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
@ -35,7 +34,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
@ -365,19 +363,6 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
loggerProviderBuilder = loggerProviderCustomizer.apply(loggerProviderBuilder, config);
SdkLoggerProvider loggerProvider = loggerProviderBuilder.build();
if (registerShutdownHook) {
Runtime.getRuntime()
.addShutdownHook(
new Thread(
() -> {
List<CompletableResultCode> shutdown = new ArrayList<>();
shutdown.add(tracerProvider.shutdown());
shutdown.add(meterProvider.shutdown());
shutdown.add(loggerProvider.shutdown());
CompletableResultCode.ofAll(shutdown).join(10, TimeUnit.SECONDS);
}));
}
ContextPropagators propagators =
PropagatorConfiguration.configurePropagators(
config, serviceClassLoader, propagatorCustomizer);
@ -390,6 +375,10 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
.setPropagators(propagators);
openTelemetrySdk = sdkBuilder.build();
if (registerShutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread(openTelemetrySdk::close));
}
}
if (setResultAsGlobal) {

View File

@ -413,7 +413,7 @@ class AutoConfiguredOpenTelemetrySdkTest {
assertThat(spanData.getResource().getAttribute(stringKey("cat"))).isEqualTo("meow");
// Ensures the export happened.
sdk.getSdkTracerProvider().shutdown().join(10, TimeUnit.SECONDS);
sdk.shutdown().join(10, TimeUnit.SECONDS);
}
@Test

View File

@ -163,21 +163,7 @@ class FullConfigTest {
@AfterEach
void afterEach() {
autoConfiguredOpenTelemetrySdk
.getOpenTelemetrySdk()
.getSdkMeterProvider()
.shutdown()
.join(10, TimeUnit.SECONDS);
autoConfiguredOpenTelemetrySdk
.getOpenTelemetrySdk()
.getSdkLoggerProvider()
.shutdown()
.join(10, TimeUnit.SECONDS);
autoConfiguredOpenTelemetrySdk
.getOpenTelemetrySdk()
.getSdkTracerProvider()
.shutdown()
.join(10, TimeUnit.SECONDS);
autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().shutdown().join(10, TimeUnit.SECONDS);
GlobalOpenTelemetry.resetForTest();
GlobalLoggerProvider.resetForTest();
}

View File

@ -12,14 +12,25 @@ import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
/** The SDK implementation of {@link OpenTelemetry}. */
@ThreadSafe
public final class OpenTelemetrySdk implements OpenTelemetry {
public final class OpenTelemetrySdk implements OpenTelemetry, Closeable {
private static final Logger LOGGER = Logger.getLogger(OpenTelemetrySdk.class.getName());
private final AtomicBoolean isShutdown = new AtomicBoolean(false);
private final ObfuscatedTracerProvider tracerProvider;
private final ObfuscatedMeterProvider meterProvider;
private final SdkLoggerProvider loggerProvider;
@ -78,6 +89,29 @@ public final class OpenTelemetrySdk implements OpenTelemetry {
return propagators;
}
/**
* Shutdown the SDK. Calls {@link SdkTracerProvider#shutdown()}, {@link
* SdkMeterProvider#shutdown()}, and {@link SdkLoggerProvider#shutdown()}.
*
* @return a {@link CompletableResultCode} which completes when all providers are shutdown
*/
public CompletableResultCode shutdown() {
if (!isShutdown.compareAndSet(false, true)) {
LOGGER.info("Multiple shutdown calls");
return CompletableResultCode.ofSuccess();
}
List<CompletableResultCode> results = new ArrayList<>();
results.add(tracerProvider.unobfuscate().shutdown());
results.add(meterProvider.unobfuscate().shutdown());
results.add(loggerProvider.shutdown());
return CompletableResultCode.ofAll(results);
}
@Override
public void close() {
shutdown().join(10, TimeUnit.SECONDS);
}
@Override
public String toString() {
return "OpenTelemetrySdk{"

View File

@ -10,14 +10,18 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
@ -36,15 +40,21 @@ import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class OpenTelemetrySdkTest {
@RegisterExtension
LogCapturer logCapturer = LogCapturer.create().captureForLogger(OpenTelemetrySdk.class.getName());
@Mock private MetricExporter metricExporter;
@Mock private SdkTracerProvider tracerProvider;
@Mock private SdkMeterProvider meterProvider;
@ -318,6 +328,42 @@ class OpenTelemetrySdkTest {
.build();
}
@Test
void shutdown() {
when(tracerProvider.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
when(meterProvider.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
when(loggerProvider.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
OpenTelemetrySdk sdk =
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.setLoggerProvider(loggerProvider)
.build();
// First call should call shutdown
assertThat(sdk.shutdown().join(10, TimeUnit.SECONDS).isSuccess()).isTrue();
verify(tracerProvider).shutdown();
verify(meterProvider).shutdown();
verify(loggerProvider).shutdown();
assertThat(logCapturer.getEvents()).isEmpty();
// Subsequent calls should log not call shutdown
Mockito.reset(tracerProvider, meterProvider, loggerProvider);
assertThat(sdk.shutdown().join(10, TimeUnit.SECONDS).isSuccess()).isTrue();
sdk.close();
verify(tracerProvider, never()).shutdown();
verify(meterProvider, never()).shutdown();
verify(loggerProvider, never()).shutdown();
assertThat(logCapturer.getEvents())
.hasSize(2)
.allSatisfy(
loggingEvent ->
assertThat(loggingEvent.getMessage()).isEqualTo("Multiple shutdown calls"));
}
@Test
void stringRepresentation() {
SpanExporter spanExporter = mock(SpanExporter.class);