From c8e1e3619e922981922a1034ce07e2a13e99d819 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Thu, 7 Dec 2023 22:41:03 +0100 Subject: [PATCH] Improve autoconfiguration capabilities for adding, delaying or dropping spans (#5986) --- ...emetry-sdk-extension-autoconfigure-spi.txt | 5 +- ...ntelemetry-sdk-extension-autoconfigure.txt | 5 +- .../spi/AutoConfigurationCustomizer.java | 34 +++++++++++++ ...AutoConfiguredOpenTelemetrySdkBuilder.java | 49 +++++++++++++++++++ .../LoggerProviderConfiguration.java | 13 ++++- .../TracerProviderConfiguration.java | 13 ++++- .../AutoConfiguredOpenTelemetrySdkTest.java | 49 +++++++++++++++++++ .../LoggerProviderConfigurationTest.java | 1 + .../TracerProviderConfigurationTest.java | 1 + .../LoggerProviderConfigurationTest.java | 1 + 10 files changed, 165 insertions(+), 6 deletions(-) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt index df26146497b..9f4a3fbb37a 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt @@ -1,2 +1,5 @@ Comparing source compatibility of against -No changes. \ No newline at end of file +*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer addLogRecordProcessorCustomizer(java.util.function.BiFunction) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer addSpanProcessorCustomizer(java.util.function.BiFunction) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt index df26146497b..c4389093f93 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt @@ -1,2 +1,5 @@ Comparing source compatibility of against -No changes. \ No newline at end of file +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer addLogRecordProcessorCustomizer(java.util.function.BiFunction) + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder addSpanProcessorCustomizer(java.util.function.BiFunction) diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/AutoConfigurationCustomizer.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/AutoConfigurationCustomizer.java index e016e6a7424..e49ab220875 100644 --- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/AutoConfigurationCustomizer.java +++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/AutoConfigurationCustomizer.java @@ -6,12 +6,14 @@ package io.opentelemetry.sdk.autoconfigure.spi; import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; import io.opentelemetry.sdk.logs.export.LogRecordExporter; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.util.Map; @@ -62,6 +64,22 @@ AutoConfigurationCustomizer addSpanExporterCustomizer( BiFunction exporterCustomizer); + /** + * Adds a {@link BiFunction} to invoke for all autoconfigured {@link + * io.opentelemetry.sdk.trace.SpanProcessor}. The return value of the {@link BiFunction} will + * replace the passed-in argument. In contrast to {@link #addSpanExporterCustomizer(BiFunction)} + * this allows modifications to happen before batching occurs. As a result, it is possible to + * efficiently filter spans, add artificial spans or delay spans for enhancing them with external, + * delayed data. + * + *

Multiple calls will execute the customizers in order. + */ + default AutoConfigurationCustomizer addSpanProcessorCustomizer( + BiFunction + spanProcessorCustomizer) { + return this; + } + /** * Adds a {@link Supplier} of a map of property names and values to use as defaults for the {@link * ConfigProperties} used during auto-configuration. The order of precedence of properties is @@ -164,4 +182,20 @@ default AutoConfigurationCustomizer addLogRecordExporterCustomizer( exporterCustomizer) { return this; } + + /** + * Adds a {@link BiFunction} to invoke for all autoconfigured {@link + * io.opentelemetry.sdk.logs.LogRecordProcessor}s. The return value of the {@link BiFunction} will + * replace the passed-in argument. In contrast to {@link + * #addLogRecordExporterCustomizer(BiFunction)} (BiFunction)} this allows modifications to happen + * before batching occurs. As a result, it is possible to efficiently filter logs, add artificial + * logs or delay logs for enhancing them with external, delayed data. + * + *

Multiple calls will execute the customizers in order. + */ + default AutoConfigurationCustomizer addLogRecordProcessorCustomizer( + BiFunction + logRecordProcessorCustomizer) { + return this; + } } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 1761108081b..2d10524e276 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -20,6 +20,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; import io.opentelemetry.sdk.logs.export.LogRecordExporter; @@ -30,6 +31,7 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.io.Closeable; @@ -71,6 +73,9 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur propagatorCustomizer = (a, unused) -> a; private BiFunction spanExporterCustomizer = (a, unused) -> a; + + private BiFunction + spanProcessorCustomizer = (a, unused) -> a; private BiFunction samplerCustomizer = (a, unused) -> a; @@ -83,6 +88,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur loggerProviderCustomizer = (a, unused) -> a; private BiFunction logRecordExporterCustomizer = (a, unused) -> a; + private BiFunction + logRecordProcessorCustomizer = (a, unused) -> a; private BiFunction resourceCustomizer = (a, unused) -> a; @@ -191,6 +198,26 @@ public AutoConfiguredOpenTelemetrySdkBuilder addSpanExporterCustomizer( return this; } + /** + * Adds a {@link BiFunction} to invoke for all autoconfigured {@link + * io.opentelemetry.sdk.trace.SpanProcessor}. The return value of the {@link BiFunction} will + * replace the passed-in argument. In contrast to {@link #addSpanExporterCustomizer(BiFunction)} + * this allows modifications to happen before batching occurs. As a result, it is possible to + * efficiently filter spans, add artificial spans or delay spans for enhancing them with external, + * delayed data. + * + *

Multiple calls will execute the customizers in order. + */ + @Override + public AutoConfiguredOpenTelemetrySdkBuilder addSpanProcessorCustomizer( + BiFunction + spanProcessorCustomizer) { + requireNonNull(spanProcessorCustomizer, "spanProcessorCustomizer"); + this.spanProcessorCustomizer = + mergeCustomizer(this.spanProcessorCustomizer, spanProcessorCustomizer); + return this; + } + /** * Adds a {@link Supplier} of a map of property names and values to use as defaults for the {@link * ConfigProperties} used during auto-configuration. The order of precedence of properties is @@ -289,6 +316,26 @@ public AutoConfiguredOpenTelemetrySdkBuilder addLogRecordExporterCustomizer( return this; } + /** + * Adds a {@link BiFunction} to invoke for all autoconfigured {@link + * io.opentelemetry.sdk.logs.LogRecordProcessor}s. The return value of the {@link BiFunction} will + * replace the passed-in argument. In contrast to {@link + * #addLogRecordExporterCustomizer(BiFunction)} (BiFunction)} this allows modifications to happen + * before batching occurs. As a result, it is possible to efficiently filter logs, add artificial + * logs or delay logs for enhancing them with external, delayed data. + * + *

Multiple calls will execute the customizers in order. + */ + @Override + public AutoConfigurationCustomizer addLogRecordProcessorCustomizer( + BiFunction + logRecordProcessorCustomizer) { + requireNonNull(logRecordProcessorCustomizer, "logRecordProcessorCustomizer"); + this.logRecordProcessorCustomizer = + mergeCustomizer(this.logRecordProcessorCustomizer, logRecordProcessorCustomizer); + return this; + } + /** * Disable the registration of a shutdown hook to shut down the SDK when appropriate. By default, * the shutdown hook is registered. @@ -372,6 +419,7 @@ public AutoConfiguredOpenTelemetrySdk build() { spiHelper, meterProvider, spanExporterCustomizer, + spanProcessorCustomizer, samplerCustomizer, closeables); tracerProviderBuilder = tracerProviderCustomizer.apply(tracerProviderBuilder, config); @@ -386,6 +434,7 @@ public AutoConfiguredOpenTelemetrySdk build() { spiHelper, meterProvider, logRecordExporterCustomizer, + logRecordProcessorCustomizer, closeables); loggerProviderBuilder = loggerProviderCustomizer.apply(loggerProviderBuilder, config); SdkLoggerProvider loggerProvider = loggerProviderBuilder.build(); diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfiguration.java index 82ffec2c33e..ff94550ecbb 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfiguration.java @@ -35,6 +35,8 @@ static void configureLoggerProvider( MeterProvider meterProvider, BiFunction logRecordExporterCustomizer, + BiFunction + logRecordProcessorCustomizer, List closeables) { loggerProviderBuilder.setLogLimits(() -> configureLogLimits(config)); @@ -42,8 +44,15 @@ static void configureLoggerProvider( Map exportersByName = configureLogRecordExporters(config, spiHelper, logRecordExporterCustomizer, closeables); - configureLogRecordProcessors(config, exportersByName, meterProvider, closeables) - .forEach(loggerProviderBuilder::addLogRecordProcessor); + List processors = + configureLogRecordProcessors(config, exportersByName, meterProvider, closeables); + for (LogRecordProcessor processor : processors) { + LogRecordProcessor wrapped = logRecordProcessorCustomizer.apply(processor, config); + if (wrapped != processor) { + closeables.add(wrapped); + } + loggerProviderBuilder.addLogRecordProcessor(wrapped); + } } // Visible for testing diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfiguration.java index 78de1700ed9..1241acccc8d 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfiguration.java @@ -40,6 +40,8 @@ static void configureTracerProvider( MeterProvider meterProvider, BiFunction spanExporterCustomizer, + BiFunction + spanProcessorCustomizer, BiFunction samplerCustomizer, List closeables) { @@ -53,8 +55,15 @@ static void configureTracerProvider( SpanExporterConfiguration.configureSpanExporters( config, spiHelper, spanExporterCustomizer, closeables); - configureSpanProcessors(config, exportersByName, meterProvider, closeables) - .forEach(tracerProviderBuilder::addSpanProcessor); + List processors = + configureSpanProcessors(config, exportersByName, meterProvider, closeables); + for (SpanProcessor processor : processors) { + SpanProcessor wrapped = spanProcessorCustomizer.apply(processor, config); + if (wrapped != processor) { + closeables.add(wrapped); + } + tracerProviderBuilder.addSpanProcessor(wrapped); + } } static List configureSpanProcessors( diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java index 649ad70406b..01047907fca 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java @@ -11,6 +11,7 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -18,6 +19,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import io.github.netmikey.logunit.api.LogCapturer; @@ -49,8 +51,11 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.opentelemetry.sdk.trace.IdGenerator; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; @@ -268,6 +273,48 @@ void builder_addSpanExporterCustomizer() { .isEqualTo(spanExporter2); } + @Test + void builder_addSpanProcessorCustomizer() { + SpanProcessor mockProcessor1 = Mockito.mock(SpanProcessor.class); + SpanProcessor mockProcessor2 = Mockito.mock(SpanProcessor.class); + doReturn(true).when(mockProcessor2).isStartRequired(); + doReturn(true).when(mockProcessor2).isEndRequired(); + Mockito.lenient().doReturn(CompletableResultCode.ofSuccess()).when(mockProcessor2).shutdown(); + Mockito.lenient().when(spanExporter1.shutdown()).thenReturn(CompletableResultCode.ofSuccess()); + + SdkTracerProvider sdkTracerProvider = + builder + .addSpanExporterCustomizer((prev, config) -> spanExporter1) + .addSpanProcessorCustomizer( + (previous, config) -> { + assertThat(previous).isNotSameAs(mockProcessor2); + return mockProcessor1; + }) + .addSpanProcessorCustomizer( + (previous, config) -> { + assertThat(previous).isSameAs(mockProcessor1); + return mockProcessor2; + }) + .build() + .getOpenTelemetrySdk() + .getSdkTracerProvider(); + + assertThat(sdkTracerProvider) + .extracting("sharedState") + .extracting("activeSpanProcessor") + .isSameAs(mockProcessor2); + + Span span = sdkTracerProvider.get("dummy-scope").spanBuilder("dummy-span").startSpan(); + + verify(mockProcessor2).onStart(any(), same((ReadWriteSpan) span)); + + span.end(); + verify(mockProcessor2).onEnd(same((ReadableSpan) span)); + + verifyNoInteractions(mockProcessor1); + verifyNoInteractions(spanExporter1); + } + @Test void builder_addPropertiesSupplier() { AutoConfiguredOpenTelemetrySdk autoConfigured = @@ -352,6 +399,8 @@ void builder_addLoggerProviderCustomizer() { // TODO: add test for addLogRecordExporterCustomizer once OTLP export is enabled by default + // TODO: add test for addLogRecordProcessorCustomizer once OTLP export is enabled by default + @Test void builder_setResultAsGlobalFalse() { GlobalOpenTelemetry.set(OpenTelemetry.noop()); diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java index 3bf772665b8..f28bb44f3fd 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java @@ -48,6 +48,7 @@ void configureLoggerProvider() { SpiHelper.create(LoggerProviderConfiguration.class.getClassLoader()), MeterProvider.noop(), (a, unused) -> a, + (a, unused) -> a, closeables); cleanup.addCloseables(closeables); diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfigurationTest.java index 3ce674d37e5..d12ab1744f4 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/TracerProviderConfigurationTest.java @@ -79,6 +79,7 @@ void configureTracerProvider() { MeterProvider.noop(), (a, unused) -> a, (a, unused) -> a, + (a, unused) -> a, closeables); try (SdkTracerProvider tracerProvider = tracerProviderBuilder.build()) { diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java index 0e1fc3e8a83..6603abf7497 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfigurationTest.java @@ -47,6 +47,7 @@ void configureLoggerProvider() { SpiHelper.create(LoggerProviderConfiguration.class.getClassLoader()), MeterProvider.noop(), (a, unused) -> a, + (a, unused) -> a, closeables); cleanup.addCloseables(closeables);