diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java index bcf70483e..2322b142f 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java @@ -157,16 +157,19 @@ private void setJfrContextStorageEnabled(boolean enabled) { * request. */ private void tryStart() { - updateJvmMemoryMetrics(); if (isJfrRecordingActive()) { logger.fine("JFR is already running, not starting again."); return; } + + updateJvmMemoryMetrics(); + if (!jfr.isAvailable()) { logger.warning( "JDK Flight Recorder (JFR) is not available in this JVM. Profiling will not start."); return; } + configSupplier.get().log(); setJfrContextStorageEnabled(true); activateJfrRecording(getResource(sdk)); diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTracker.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTracker.java index ecbaadfa7..1c274257e 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTracker.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTracker.java @@ -31,6 +31,8 @@ class ActiveSpanTracker implements ContextStorage, SpanTracker { private final ContextStorage delegate; private final TraceRegistry registry; + private boolean enabled; + ActiveSpanTracker(ContextStorage delegate, TraceRegistry registry) { this.delegate = delegate; this.registry = registry; @@ -39,6 +41,10 @@ class ActiveSpanTracker implements ContextStorage, SpanTracker { @Override public Scope attach(Context toAttach) { Scope scope = delegate.attach(toAttach); + if (!isEnabled()) { + return scope; + } + SpanContext newSpanContext = Span.fromContext(toAttach).getSpanContext(); if (doNotTrack(newSpanContext)) { return scope; @@ -61,6 +67,16 @@ public Scope attach(Context toAttach) { }; } + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public boolean isEnabled() { + return enabled; + } + private boolean doNotTrack(SpanContext spanContext) { return !spanContext.isSampled() || !registry.isRegistered(spanContext); } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapper.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapper.java index a712d1c9f..96aee14a1 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapper.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapper.java @@ -49,6 +49,9 @@ private ContextStorage trackActiveSpans(ContextStorage storage, TraceRegistry re } private ContextStorage detectThreadChanges(ContextStorage storage, TraceRegistry registry) { - return new TraceThreadChangeDetector(storage, registry, StackTraceSampler.SUPPLIER); + TraceThreadChangeDetector detector = + new TraceThreadChangeDetector(storage, registry, StackTraceSampler.SUPPLIER); + TraceThreadChangeDetector.SUPPLIER.configure(detector); + return detector; } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java new file mode 100644 index 000000000..cf0653b9a --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java @@ -0,0 +1,53 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.opentelemetry.profiler.snapshot; + +import com.google.auto.service.AutoService; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.javaagent.extension.AgentListener; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.util.function.Function; + +@AutoService(AgentListener.class) +public class SnapshotProfilingAgentListener implements AgentListener { + private final Function + snapshotProfilingSupervisorMaker; + + public SnapshotProfilingAgentListener() { + this(SnapshotProfilingSupervisor::initialize); + } + + @VisibleForTesting + SnapshotProfilingAgentListener( + Function + snapshotProfilingSupervisorMaker) { + this.snapshotProfilingSupervisorMaker = snapshotProfilingSupervisorMaker; + } + + @Override + public void afterAgent(AutoConfiguredOpenTelemetrySdk sdk) { + // Must be always executed to initialize supervisor + SnapshotProfilingSupervisor supervisor = snapshotProfilingSupervisorMaker.apply(sdk); + + SnapshotProfilingConfiguration configuration = SnapshotProfilingConfiguration.SUPPLIER.get(); + if (!configuration.isEnabled()) { + return; + } + + supervisor.startProfiling(); + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProvider.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProvider.java index 84d6d737b..96f2c853d 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProvider.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProvider.java @@ -49,19 +49,12 @@ OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel m SnapshotProfilingConfiguration snapshotProfiling = getSnapshotProfilingConfig(model); SnapshotProfilingConfiguration.SUPPLIER.configure(snapshotProfiling); - if (snapshotProfiling.isEnabled()) { - initActiveSpansTracking(); - initStackTraceSampler(snapshotProfiling); - addSpanProcessors(model); - } + initActiveSpansTracking(); + addSpanProcessors(model); return model; } - private void initStackTraceSampler(SnapshotProfilingConfiguration snapshotProfilingConfig) { - StackTraceSamplerInitializer.setupStackTraceSampler(snapshotProfilingConfig); - } - private void addSpanProcessors(OpenTelemetryConfigurationModel model) { TracerProviderModel tracerProviderModel = model.getTracerProvider(); if (tracerProviderModel == null) { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java index 8bfcd76fa..f20db99a6 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java @@ -22,56 +22,24 @@ import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; -import java.time.Duration; import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; @AutoService(AutoConfigurationCustomizerProvider.class) public class SnapshotProfilingSdkCustomizer implements AutoConfigurationCustomizerProvider { private final TraceRegistry registry; - private final Function samplerProvider; private final ContextStorageWrapper contextStorageWrapper; public SnapshotProfilingSdkCustomizer() { - this( - TraceRegistryHolder.getTraceRegistry(), - stackTraceSamplerProvider(), - new ContextStorageWrapper()); - } - - private static Function stackTraceSamplerProvider() { - return properties -> { - SnapshotProfilingConfiguration configuration = SnapshotProfilingConfiguration.SUPPLIER.get(); - Duration samplingPeriod = configuration.getSamplingInterval(); - StagingArea.SUPPLIER.configure(createStagingArea(configuration)); - return new PeriodicStackTraceSampler( - StagingArea.SUPPLIER, SpanTracker.SUPPLIER, samplingPeriod); - }; - } - - private static StagingArea createStagingArea(SnapshotProfilingConfiguration configuration) { - Duration interval = configuration.getExportInterval(); - int capacity = configuration.getStagingCapacity(); - return new PeriodicallyExportingStagingArea(StackTraceExporter.SUPPLIER, interval, capacity); + this(TraceRegistryHolder.getTraceRegistry(), new ContextStorageWrapper()); } @VisibleForTesting SnapshotProfilingSdkCustomizer( - TraceRegistry registry, - StackTraceSampler sampler, - ContextStorageWrapper contextStorageWrapper) { - this(registry, properties -> sampler, contextStorageWrapper); - } - - private SnapshotProfilingSdkCustomizer( - TraceRegistry registry, - Function samplerProvider, - ContextStorageWrapper contextStorageWrapper) { + TraceRegistry registry, ContextStorageWrapper contextStorageWrapper) { this.registry = registry; - this.samplerProvider = samplerProvider; this.contextStorageWrapper = contextStorageWrapper; } @@ -82,7 +50,6 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { autoConfigurationCustomizer .addTracerProviderCustomizer(snapshotProfilingSpanProcessor(registry)) - .addPropertiesCustomizer(setupStackTraceSampler()) .addPropertiesCustomizer(startTrackingActiveSpans(registry)) .addTracerProviderCustomizer(addShutdownHook()); } @@ -99,9 +66,7 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { private BiFunction addShutdownHook() { return (builder, properties) -> { - if (snapshotProfilingEnabled()) { - builder.addSpanProcessor(new SdkShutdownHook()); - } + builder.addSpanProcessor(new SdkShutdownHook()); return builder; }; } @@ -109,44 +74,20 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { private BiFunction snapshotProfilingSpanProcessor(TraceRegistry registry) { return (builder, properties) -> { - if (snapshotProfilingEnabled()) { - double selectionProbability = - SnapshotProfilingConfiguration.SUPPLIER.get().getSnapshotSelectionProbability(); - - return builder.addSpanProcessor( - new SnapshotProfilingSpanProcessor( - registry, new TraceIdBasedSnapshotSelector(selectionProbability))); - } - return builder; - }; - } - - private boolean includeTraceContextPropagator(Set configuredPropagators) { - return configuredPropagators.isEmpty(); - } + double selectionProbability = + SnapshotProfilingConfiguration.SUPPLIER.get().getSnapshotSelectionProbability(); - private Function> setupStackTraceSampler() { - return properties -> { - if (snapshotProfilingEnabled()) { - StackTraceSampler sampler = samplerProvider.apply(properties); - ConfigurableSupplier supplier = StackTraceSampler.SUPPLIER; - supplier.configure(sampler); - } - return Collections.emptyMap(); + return builder.addSpanProcessor( + new SnapshotProfilingSpanProcessor( + registry, new TraceIdBasedSnapshotSelector(selectionProbability))); }; } private Function> startTrackingActiveSpans( TraceRegistry registry) { return properties -> { - if (snapshotProfilingEnabled()) { - contextStorageWrapper.wrapContextStorage(registry); - } + contextStorageWrapper.wrapContextStorage(registry); return Collections.emptyMap(); }; } - - private boolean snapshotProfilingEnabled() { - return SnapshotProfilingConfiguration.SUPPLIER.get().isEnabled(); - } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java new file mode 100644 index 000000000..0a0161c7a --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java @@ -0,0 +1,116 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.opentelemetry.profiler.snapshot; + +import com.google.common.annotations.VisibleForTesting; +import com.splunk.opentelemetry.profiler.OtelLoggerFactory; +import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; +import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.util.logging.Logger; + +public class SnapshotProfilingSupervisor { + public static final OptionalConfigurableSupplier SUPPLIER = + new OptionalConfigurableSupplier<>(); + private static final Logger logger = + Logger.getLogger(SnapshotProfilingSupervisor.class.getName()); + + private final OptionalConfigurableSupplier configurationSupplier; + private final ConfigurableSupplier spanTrackerSupplier; + private final OptionalConfigurableSupplier + traceThreadChangeDetectorSupplier; + private final AutoConfiguredOpenTelemetrySdk sdk; + private final OtelLoggerFactory otelLoggerFactory; + private boolean running; + + @VisibleForTesting + SnapshotProfilingSupervisor( + OptionalConfigurableSupplier configurationSupplier, + ConfigurableSupplier spanTrackerSupplier, + OptionalConfigurableSupplier traceThreadChangeDetectorSupplier, + AutoConfiguredOpenTelemetrySdk sdk, + OtelLoggerFactory otelLoggerFactory) { + this.configurationSupplier = configurationSupplier; + this.spanTrackerSupplier = spanTrackerSupplier; + this.traceThreadChangeDetectorSupplier = traceThreadChangeDetectorSupplier; + this.sdk = sdk; + this.otelLoggerFactory = otelLoggerFactory; + } + + public static SnapshotProfilingSupervisor initialize(AutoConfiguredOpenTelemetrySdk sdk) { + if (SUPPLIER.isConfigured()) { + throw new IllegalStateException("Snapshot profiling already initialized"); + } + + SnapshotProfilingSupervisor supervisor = + new SnapshotProfilingSupervisor( + SnapshotProfilingConfiguration.SUPPLIER, + SpanTracker.SUPPLIER, + TraceThreadChangeDetector.SUPPLIER, + sdk, + new OtelLoggerFactory()); + SUPPLIER.configure(supervisor); + + return supervisor; + } + + public synchronized void startProfiling() { + if (running) { + return; + } + + configurationSupplier.get().log(); + + // StagingArea.SUPPLIER + // StackTraceSampler.SUPPLIER + // StackTraceExporter.SUPPLIER = AsyncStackTraceExporter + StackTraceSamplerInitializer.setupStackTraceSampler(configurationSupplier.get()); + StackTraceSamplerInitializer.setupStackTraceExporter( + configurationSupplier.get(), AutoConfigureUtil.getResource(sdk), otelLoggerFactory); + + // SpanTracker.SUPPLIER + spanTrackerSupplier.get().setEnabled(true); + traceThreadChangeDetectorSupplier.get().setEnabled(true); + + // SnapshotProfilingSdkCustomizer/SnapshotProfilingSpanProcessorComponentProvider -> + // SnapshotProfilingSpanProcessor + // SdkShutdownHookComponentProvider -> SdkShutdownHook + + running = true; + logger.info("Snapshot profiling is active."); + } + + public synchronized void stopProfiling() { + if (!running) { + return; + } + + StackTraceSampler.SUPPLIER.get().close(); + StackTraceSampler.SUPPLIER.reset(); + + StagingArea.SUPPLIER.get().close(); + StagingArea.SUPPLIER.reset(); + + StackTraceExporter.SUPPLIER.get().close(); + StackTraceExporter.SUPPLIER.reset(); + + SpanTracker.SUPPLIER.get().setEnabled(false); + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(false); + + running = false; + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SpanTracker.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SpanTracker.java index 6c5f370d5..31ed12b7f 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SpanTracker.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SpanTracker.java @@ -20,8 +20,27 @@ import java.util.Optional; interface SpanTracker { - SpanTracker NOOP = thread -> Optional.empty(); + SpanTracker NOOP = + new SpanTracker() { + @Override + public Optional getActiveSpan(Thread thread) { + return Optional.empty(); + } + + @Override + public void setEnabled(boolean enabled) {} + + @Override + public boolean isEnabled() { + return false; + } + }; + ConfigurableSupplier SUPPLIER = new ConfigurableSupplier<>(SpanTracker.NOOP); Optional getActiveSpan(Thread thread); + + void setEnabled(boolean enabled); + + boolean isEnabled(); } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java deleted file mode 100644 index 1f474d529..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.profiler.snapshot; - -import com.google.auto.service.AutoService; -import com.google.common.annotations.VisibleForTesting; -import com.splunk.opentelemetry.profiler.OtelLoggerFactory; -import com.splunk.opentelemetry.profiler.util.DeclarativeConfigPropertiesUtil; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.api.logs.Logger; -import io.opentelemetry.javaagent.extension.AgentListener; -import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; -import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.resources.Resource; - -@AutoService(AgentListener.class) -public class StackTraceExporterActivator implements AgentListener { - private static final java.util.logging.Logger logger = - java.util.logging.Logger.getLogger(StackTraceExporterActivator.class.getName()); - - private final OtelLoggerFactory otelLoggerFactory; - - public StackTraceExporterActivator() { - this(new OtelLoggerFactory()); - } - - @VisibleForTesting - StackTraceExporterActivator(OtelLoggerFactory otelLoggerFactory) { - this.otelLoggerFactory = otelLoggerFactory; - } - - @Override - public void afterAgent(AutoConfiguredOpenTelemetrySdk sdk) { - SnapshotProfilingConfiguration configuration = SnapshotProfilingConfiguration.SUPPLIER.get(); - if (!configuration.isEnabled()) { - return; - } - - configuration.log(); - - int maxDepth = configuration.getStackDepth(); - Logger otelLogger = buildLogger(sdk, configuration.getConfigProperties()); - AsyncStackTraceExporter exporter = new AsyncStackTraceExporter(otelLogger, maxDepth); - StackTraceExporter.SUPPLIER.configure(exporter); - - logger.info("Snapshot profiling is active."); - } - - private Logger buildLogger(AutoConfiguredOpenTelemetrySdk sdk, Object configProperties) { - Resource resource = AutoConfigureUtil.getResource(sdk); - if (configProperties instanceof DeclarativeConfigProperties) { - DeclarativeConfigProperties exporterConfig = - DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( - (DeclarativeConfigProperties) configProperties, "exporter"); - return otelLoggerFactory.build(exporterConfig, resource); - } - if (configProperties instanceof ConfigProperties) { - return otelLoggerFactory.build((ConfigProperties) configProperties, resource); - } - throw new IllegalArgumentException( - "Unsupported config properties type: " + configProperties.getClass().getName()); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceSamplerInitializer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceSamplerInitializer.java index 6593de656..d6ba287a1 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceSamplerInitializer.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceSamplerInitializer.java @@ -16,10 +16,16 @@ package com.splunk.opentelemetry.profiler.snapshot; +import com.splunk.opentelemetry.profiler.OtelLoggerFactory; +import com.splunk.opentelemetry.profiler.util.DeclarativeConfigPropertiesUtil; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; import java.time.Duration; class StackTraceSamplerInitializer { - public StackTraceSamplerInitializer() {} + private StackTraceSamplerInitializer() {} static void setupStackTraceSampler(SnapshotProfilingConfiguration configuration) { Duration samplingPeriod = configuration.getSamplingInterval(); @@ -31,9 +37,35 @@ static void setupStackTraceSampler(SnapshotProfilingConfiguration configuration) StackTraceSampler.SUPPLIER.configure(sampler); } + static void setupStackTraceExporter( + SnapshotProfilingConfiguration configuration, + Resource resource, + OtelLoggerFactory otelLoggerFactory) { + int maxDepth = configuration.getStackDepth(); + Logger otelLogger = + buildLogger(otelLoggerFactory, resource, configuration.getConfigProperties()); + AsyncStackTraceExporter exporter = new AsyncStackTraceExporter(otelLogger, maxDepth); + StackTraceExporter.SUPPLIER.configure(exporter); + } + private static StagingArea createStagingArea(SnapshotProfilingConfiguration configuration) { Duration interval = configuration.getExportInterval(); int capacity = configuration.getStagingCapacity(); return new PeriodicallyExportingStagingArea(StackTraceExporter.SUPPLIER, interval, capacity); } + + private static Logger buildLogger( + OtelLoggerFactory otelLoggerFactory, Resource resource, Object configProperties) { + if (configProperties instanceof DeclarativeConfigProperties) { + DeclarativeConfigProperties exporterConfig = + DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( + (DeclarativeConfigProperties) configProperties, "exporter"); + return otelLoggerFactory.build(exporterConfig, resource); + } + if (configProperties instanceof ConfigProperties) { + return otelLoggerFactory.build((ConfigProperties) configProperties, resource); + } + throw new IllegalArgumentException( + "Unsupported config properties type: " + configProperties.getClass().getName()); + } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetector.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetector.java index 5ab79f00f..40147cf73 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetector.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetector.java @@ -16,6 +16,7 @@ package com.splunk.opentelemetry.profiler.snapshot; +import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; @@ -25,10 +26,15 @@ import javax.annotation.Nullable; class TraceThreadChangeDetector implements ContextStorage { + static OptionalConfigurableSupplier SUPPLIER = + new OptionalConfigurableSupplier<>(); + private final ContextStorage delegate; private final TraceRegistry registry; private final Supplier sampler; + private boolean enabled; + TraceThreadChangeDetector( ContextStorage delegate, TraceRegistry registry, Supplier sampler) { this.delegate = delegate; @@ -36,9 +42,21 @@ class TraceThreadChangeDetector implements ContextStorage { this.sampler = sampler; } + void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + boolean isEnabled() { + return enabled; + } + @Override public Scope attach(Context toAttach) { Scope scope = delegate.attach(toAttach); + if (!isEnabled()) { + return scope; + } + SpanContext newSpanContext = Span.fromContext(toAttach).getSpanContext(); if (doNotTrack(newSpanContext)) { return scope; diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTrackerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTrackerTest.java index dc498418f..5f0417804 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTrackerTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ActiveSpanTrackerTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ActiveSpanTrackerTest { @@ -42,6 +43,11 @@ class ActiveSpanTrackerTest { private final TogglableTraceRegistry registry = new TogglableTraceRegistry(); private final ActiveSpanTracker spanTracker = new ActiveSpanTracker(storage, registry); + @BeforeEach + void setUp() { + spanTracker.setEnabled(true); + } + @Test void currentContextComesFromOpenTelemetryContextStorage() { var context = Context.root().with(ContextKey.named("test-key"), "value"); @@ -209,6 +215,19 @@ void doNotTrackSpanWhenNoSpanPresentInContext() { } } + @Test + void doNotTrackSpanWhenTrackerIsDisabled() { + spanTracker.setEnabled(false); + + var span = Span.wrap(Snapshotting.spanContext().build()); + var context = Context.root().with(span); + registry.register(span.getSpanContext()); + + try (var ignored = spanTracker.attach(context)) { + assertEquals(Optional.empty(), spanTracker.getActiveSpan(Thread.currentThread())); + } + } + @Test void doNotTrackSpanWhenSpanIsNotSampled() { var span = Span.wrap(Snapshotting.spanContext().unsampled().build()); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java index 70f6a8934..2214ef320 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java @@ -19,7 +19,6 @@ import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; -import com.splunk.opentelemetry.profiler.OtelLoggerFactory; import com.splunk.opentelemetry.profiler.snapshot.simulation.Delay; import com.splunk.opentelemetry.profiler.snapshot.simulation.ExitCall; import com.splunk.opentelemetry.profiler.snapshot.simulation.Message; @@ -28,13 +27,13 @@ import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.autoconfigure.OpenTelemetrySdkExtension; -import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; import java.time.Duration; import java.util.ArrayList; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.UnaryOperator; import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -43,7 +42,6 @@ class ConcurrentServiceEntrySamplingTest { private final ResettingContextStorageWrapper spanTrackingActivator = new ResettingContextStorageWrapper(); - private final InMemoryLogRecordExporter logExporter = InMemoryLogRecordExporter.create(); private final InMemoryStagingArea staging = new InMemoryStagingArea(); private final StackTraceSampler sampler = newSampler(staging); @@ -55,7 +53,7 @@ private StackTraceSampler newSampler(StagingArea staging) { } private final SnapshotProfilingSdkCustomizer downstreamCustomizer = - Snapshotting.customizer().with(sampler).with(spanTrackingActivator).build(); + Snapshotting.customizer().with(spanTrackingActivator).build(); @RegisterExtension public final OpenTelemetrySdkExtension downstreamSdk = @@ -63,10 +61,6 @@ private StackTraceSampler newSampler(StagingArea staging) { .withProperty("splunk.snapshot.profiler.enabled", "true") .withProperty("splunk.snapshot.selection.probability", "1.0") .with(downstreamCustomizer) - .with( - new StackTraceExporterActivator( - new OtelLoggerFactory( - () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); @RegisterExtension @@ -95,6 +89,12 @@ private StackTraceSampler newSampler(StagingArea staging) { .performing(concurrentExitCallsTo(2, downstream, upstreamSdk)) .build(); + @BeforeEach + void enableContextTracking() { + SpanTracker.SUPPLIER.get().setEnabled(true); + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); + } + /** * The test is attempting to model the scenario where an upstream service makes two concurrent * requests into the same downstream service within the same trace. Real scenarios may differ, but diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapperTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapperTest.java index 475a69dc6..81a72569f 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapperTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ContextStorageWrapperTest.java @@ -43,6 +43,7 @@ void installActiveSpanTracker() { registry.register(spanContext); wrapper.wrapContextStorage(registry); + SpanTracker.SUPPLIER.get().setEnabled(true); try (var ignored = Context.current().with(span).makeCurrent()) { var activeSpan = SpanTracker.SUPPLIER.get().getActiveSpan(Thread.currentThread()); assertEquals(Optional.of(spanContext), activeSpan); @@ -61,6 +62,7 @@ void installThreadChangeDetector() { StackTraceSampler.SUPPLIER.configure(sampler); wrapper.wrapContextStorage(registry); + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); try (var ignored = Context.current().with(span).makeCurrent()) { assertThat(sampler.isBeingSampled(Thread.currentThread())).isTrue(); } @@ -88,6 +90,8 @@ void spanTrackingRunsBeforeThreadChangeDetector() { StackTraceSampler.SUPPLIER.configure(sampler); wrapper.wrapContextStorage(registry); + SpanTracker.SUPPLIER.get().setEnabled(true); + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); try (var ignored = Context.current().with(span).makeCurrent()) { var activeSpan = SpanTracker.SUPPLIER.get().getActiveSpan(Thread.currentThread()).orElseThrow(); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java index 5b80afa7a..46df1a52a 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java @@ -27,6 +27,7 @@ import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.LoggerFactory; @@ -41,11 +42,17 @@ class GracefulShutdownTest { .withProperty("splunk.snapshot.selection.probability", "1.0") .with(Snapshotting.customizer().withRealStackTraceSampler().withRealStagingArea().build()) .with( - new StackTraceExporterActivator( + Snapshotting.agentListener( new OtelLoggerFactory( () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); + @AfterEach + void tearDown() { + sdk.close(); + Snapshotting.resetProfiling(); + } + @Test void stopSnapshotProfilingExtensionWhenOpenTelemetrySdkIsShutdown(Tracer tracer) throws Exception { diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/InMemorySpanTracker.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/InMemorySpanTracker.java index 829390d45..7478596ee 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/InMemorySpanTracker.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/InMemorySpanTracker.java @@ -23,6 +23,7 @@ class InMemorySpanTracker implements SpanTracker { private final Map activeSpans = new HashMap<>(); + private boolean enabled = true; void store(Thread thread, SpanContext spanContext) { activeSpans.put(thread.getId(), spanContext); @@ -32,4 +33,14 @@ void store(Thread thread, SpanContext spanContext) { public Optional getActiveSpan(Thread thread) { return Optional.ofNullable(activeSpans.get(thread.getId())); } + + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public boolean isEnabled() { + return enabled; + } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/LongRunningBackgroundTaskTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/LongRunningBackgroundTaskTest.java index 791c125c7..594bf918b 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/LongRunningBackgroundTaskTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/LongRunningBackgroundTaskTest.java @@ -26,6 +26,7 @@ import java.time.Duration; import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -48,6 +49,11 @@ class LongRunningBackgroundTaskTest { public final Server server = Server.builder(sdk).named("server").performing(Background.task(slowTask())).build(); + @BeforeEach + void enableThreadChangeDetection() { + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); + } + private Runnable slowTask() { return () -> { try { diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/MultiThreadedTraceProfilingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/MultiThreadedTraceProfilingTest.java index a5afc32e3..55f05f11a 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/MultiThreadedTraceProfilingTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/MultiThreadedTraceProfilingTest.java @@ -26,6 +26,7 @@ import java.time.Duration; import java.util.UUID; import java.util.concurrent.Callable; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -46,6 +47,11 @@ public class MultiThreadedTraceProfilingTest { public final Server server = Server.builder(sdk).named("server").performing(Background.task(slowTask())).build(); + @BeforeEach + void enableThreadChangeDetection() { + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); + } + private Callable slowTask() { return () -> { Thread.sleep(250); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java similarity index 88% rename from profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java rename to profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java index f5d3d153b..65ac03a7e 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java @@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; import com.splunk.opentelemetry.profiler.OtelLoggerFactory; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; @@ -32,20 +33,22 @@ import java.util.Collections; import java.util.List; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -class StackTraceExporterActivatorTest { +class SnapshotProfilingAgentListenerTest { @RegisterExtension public final OpenTelemetrySdkExtension sdk = OpenTelemetrySdkExtension.configure().build(); + @BeforeEach + void setUp() { + TraceThreadChangeDetector.SUPPLIER.configure(mock(TraceThreadChangeDetector.class)); + } + @AfterEach void tearDown() { - SpanTracker.SUPPLIER.reset(); - StackTraceSampler.SUPPLIER.reset(); - StagingArea.SUPPLIER.reset(); - SnapshotProfilingConfiguration.SUPPLIER.reset(); - StackTraceExporter.SUPPLIER.reset(); + Snapshotting.resetProfiling(); } @Test @@ -58,7 +61,7 @@ void configureStackTraceExporterProvider() { .build(); SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - new StackTraceExporterActivator( + Snapshotting.agentListener( new OtelLoggerFactory(() -> logExporter, declarativeConfigProperties -> logExporter)) .afterAgent(sdk); @@ -79,7 +82,7 @@ void declarativeConfigWithoutExporterPreservesComponentLoader() { .build(); SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - new StackTraceExporterActivator().afterAgent(sdk); + new SnapshotProfilingAgentListener().afterAgent(sdk); var exporter = StackTraceExporter.SUPPLIER.get(); assertNotSame(StackTraceExporter.NOOP, exporter); @@ -93,7 +96,7 @@ void doNotConfigureStackTraceExporterProvider() { SnapshotProfilingConfiguration.builder().setEnabled(false).build(); SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - new StackTraceExporterActivator().afterAgent(sdk); + new SnapshotProfilingAgentListener().afterAgent(sdk); var exporter = StackTraceExporter.SUPPLIER.get(); assertSame(StackTraceExporter.NOOP, exporter); @@ -101,7 +104,7 @@ void doNotConfigureStackTraceExporterProvider() { private static class RecordingComponentLoader implements ComponentLoader { private final ComponentLoader delegate = - ComponentLoader.forClassLoader(StackTraceExporterActivatorTest.class.getClassLoader()); + ComponentLoader.forClassLoader(SnapshotProfilingAgentListenerTest.class.getClassLoader()); private final List> loadedSpiClasses = new ArrayList<>(); @Override diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProviderTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProviderTest.java index ecb07ab81..3408bd296 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProviderTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationCustomizerProviderTest.java @@ -26,14 +26,11 @@ import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SimpleSpanProcessorModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorModel; -import java.io.IOException; -import java.nio.file.Path; import java.util.List; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; class SnapshotProfilingConfigurationCustomizerProviderTest { @RegisterExtension static final AutoCleanupExtension autoCleanup = AutoCleanupExtension.create(); @@ -44,10 +41,11 @@ void resetSuppliers() { StackTraceSampler.SUPPLIER.reset(); StagingArea.SUPPLIER.reset(); SpanTracker.SUPPLIER.reset(); + TraceThreadChangeDetector.SUPPLIER.reset(); } @Test - void shouldDoNothingIfProfilerIsNotEnabled(@TempDir Path tempDir) throws IOException { + void shouldAddComponentsNeededToEnableProfilingIfProfilerIsNotEnabledInitially() { // given String yaml = """ @@ -58,9 +56,18 @@ void shouldDoNothingIfProfilerIsNotEnabled(@TempDir Path tempDir) throws IOExcep OpenTelemetryConfigurationModel model = getCustomizedModel(yaml); // then + List expectedProcessors = + List.of( + new SpanProcessorModel() + .withAdditionalProperty(SnapshotProfilingSpanProcessorComponentProvider.NAME, null), + new SpanProcessorModel() + .withAdditionalProperty(SdkShutdownHookComponentProvider.NAME, null)); + assertThat(model).isNotNull(); assertThat(model.getPropagator()).isNull(); - assertThat(model.getTracerProvider()).isNull(); + assertThat(model.getTracerProvider()).isNotNull(); + assertThat(model.getTracerProvider().getProcessors()).hasSize(2); + assertThat(model.getTracerProvider().getProcessors()).containsAll(expectedProcessors); } @Test diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java index db71b2917..6d3900130 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java @@ -61,7 +61,7 @@ class SnapshotProfilingLogExportingTest { .withProperty("splunk.snapshot.selection.probability", "1.0") .with(customizer) .with( - new StackTraceExporterActivator( + Snapshotting.agentListener( new OtelLoggerFactory( () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerBuilder.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerBuilder.java index f6cab1f2c..b6a899717 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerBuilder.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerBuilder.java @@ -20,7 +20,6 @@ class SnapshotProfilingSdkCustomizerBuilder { private TraceRegistry registry = new TraceRegistry(); - private StackTraceSampler sampler = new ObservableStackTraceSampler(); private ContextStorageWrapper contextStorageWrapper = new ResettingContextStorageWrapper(); SnapshotProfilingSdkCustomizerBuilder with(TraceRegistry registry) { @@ -28,10 +27,6 @@ SnapshotProfilingSdkCustomizerBuilder with(TraceRegistry registry) { return this; } - SnapshotProfilingSdkCustomizer real() { - return new SnapshotProfilingSdkCustomizer(); - } - SnapshotProfilingSdkCustomizerBuilder withRealStackTraceSampler() { return withRealStackTraceSampler(Duration.ofMillis(20)); } @@ -43,7 +38,6 @@ SnapshotProfilingSdkCustomizerBuilder withRealStackTraceSampler(Duration samplin SnapshotProfilingSdkCustomizerBuilder with(StackTraceSampler sampler) { StackTraceSampler.SUPPLIER.configure(sampler); - this.sampler = sampler; return this; } @@ -67,6 +61,6 @@ SnapshotProfilingSdkCustomizerBuilder with(ContextStorageWrapper contextStorageW } SnapshotProfilingSdkCustomizer build() { - return new SnapshotProfilingSdkCustomizer(registry, sampler, contextStorageWrapper); + return new SnapshotProfilingSdkCustomizer(registry, contextStorageWrapper); } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/Snapshotting.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/Snapshotting.java index ef1324c17..f2f56cd6f 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/Snapshotting.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/Snapshotting.java @@ -16,6 +16,7 @@ package com.splunk.opentelemetry.profiler.snapshot; +import com.splunk.opentelemetry.profiler.OtelLoggerFactory; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.TraceFlags; @@ -32,6 +33,30 @@ static SnapshotProfilingSdkCustomizerBuilder customizer() { return new SnapshotProfilingSdkCustomizerBuilder(); } + static SnapshotProfilingAgentListener agentListener(OtelLoggerFactory otelLoggerFactory) { + return new SnapshotProfilingAgentListener( + sdk -> + new SnapshotProfilingSupervisor( + SnapshotProfilingConfiguration.SUPPLIER, + SpanTracker.SUPPLIER, + TraceThreadChangeDetector.SUPPLIER, + sdk, + otelLoggerFactory)); + } + + static void resetProfiling() { + if (SnapshotProfilingSupervisor.SUPPLIER.isConfigured()) { + SnapshotProfilingSupervisor.SUPPLIER.get().stopProfiling(); + SnapshotProfilingSupervisor.SUPPLIER.reset(); + } + SpanTracker.SUPPLIER.reset(); + TraceThreadChangeDetector.SUPPLIER.reset(); + StackTraceSampler.SUPPLIER.reset(); + StagingArea.SUPPLIER.reset(); + SnapshotProfilingConfiguration.SUPPLIER.reset(); + StackTraceExporter.SUPPLIER.reset(); + } + static StackTraceBuilder stackTrace() { var threadId = RANDOM.nextLong(10_000); return new StackTraceBuilder() diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceProfilingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceProfilingTest.java index 4022c6fe2..059a2f6b8 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceProfilingTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceProfilingTest.java @@ -24,6 +24,7 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.autoconfigure.OpenTelemetrySdkExtension; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -41,6 +42,11 @@ class TraceProfilingTest { .with(customizer) .build(); + @BeforeEach + void enableThreadChangeDetection() { + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); + } + @Test void startTraceProfilingWhenRootSpanContextBegins(Tracer tracer) { try (var ignored = Context.root().makeCurrent()) { diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetectorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetectorTest.java index 1ccbe582e..958aa3396 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetectorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetectorTest.java @@ -26,6 +26,7 @@ import io.opentelemetry.context.Scope; import java.util.concurrent.Callable; import java.util.concurrent.Executors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class TraceThreadChangeDetectorTest { @@ -35,6 +36,11 @@ class TraceThreadChangeDetectorTest { private final TraceThreadChangeDetector detector = new TraceThreadChangeDetector(storage, registry, () -> sampler); + @BeforeEach + void setUp() { + detector.setEnabled(true); + } + @Test void currentContextComesFromOpenTelemetryContextStorage() { var context = Context.root().with(ContextKey.named("test-key"), "value"); @@ -110,6 +116,25 @@ void doNotStartSamplingThreadWhenSpanIsNotSampled() throws Exception { } } + @Test + void doNotPerformSamplingWhenNotEnabled() throws Exception { + var spanContext = Snapshotting.spanContext().build(); + registry.register(spanContext); + sampler.start(Thread.currentThread(), spanContext); + + var span = Span.wrap(spanContext); + var context = Context.root().with(span); + + var executor = Executors.newSingleThreadExecutor(); + detector.setEnabled(false); + + try (var ts = executor.submit(captureThread(() -> detector.attach(context))).get()) { + assertThat(sampler.isBeingSampled(ts.thread)).isFalse(); + } finally { + executor.shutdownNow(); + } + } + private Callable captureThread(Callable callable) { return () -> new ThreadScope(Thread.currentThread(), callable.call()); }