From bf446976123f26eea3587a517cc817068733f50b Mon Sep 17 00:00:00 2001 From: robsunday Date: Tue, 7 Jul 2026 12:21:54 +0200 Subject: [PATCH 01/12] Snapshot Profiler Configuration refactoring --- ...DeclarativeEffectiveConfigFileFactory.java | 3 +- .../EnvVarsEffectiveConfigFileFactory.java | 3 +- ...arativeEffectiveConfigFileFactoryTest.java | 8 +- ...EnvVarsEffectiveConfigFileFactoryTest.java | 5 + .../SnapshotProfilingConfiguration.java | 189 ++++++++++++++++-- ...filingConfigurationCustomizerProvider.java | 11 +- ...shotProfilingDeclarativeConfiguration.java | 104 ---------- ...filingDeclarativeConfigurationFactory.java | 75 +++++++ ...SnapshotProfilingEnvVarsConfiguration.java | 114 ----------- ...tProfilingEnvVarsConfigurationFactory.java | 77 +++++++ .../SnapshotProfilingSdkCustomizer.java | 32 ++- ...ofilingSpanProcessorComponentProvider.java | 3 +- .../snapshot/StackTraceExporterActivator.java | 24 ++- .../profiler/JfrAgentListenerTest.java | 4 +- ...ngConfigurationCustomizerProviderTest.java | 2 +- .../SnapshotProfilingConfigurationTest.java | 83 ++++++++ ...gDeclarativeConfigurationFactoryTest.java} | 22 +- ...ilingEnvVarsConfigurationFactoryTest.java} | 109 +++++----- .../StackTraceExporterActivatorTest.java | 140 +++++++------ 19 files changed, 598 insertions(+), 410 deletions(-) delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfiguration.java create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactory.java delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfiguration.java create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactory.java create mode 100644 profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationTest.java rename profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/{SnapshotProfilingDeclarativeConfigurationTest.java => SnapshotProfilingDeclarativeConfigurationFactoryTest.java} (87%) rename profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/{SnapshotProfilingEnvVarsConfigurationTest.java => SnapshotProfilingEnvVarsConfigurationFactoryTest.java} (66%) diff --git a/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactory.java b/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactory.java index 7fe5d6ebe..44521465a 100644 --- a/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactory.java +++ b/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactory.java @@ -20,7 +20,6 @@ import com.splunk.opentelemetry.opamp.DeclarativeConfigurationInterceptor; import com.splunk.opentelemetry.profiler.ProfilerConfiguration; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; -import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingDeclarativeConfiguration; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LogRecordExporterModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LoggerProviderModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.MeterProviderModel; @@ -71,7 +70,7 @@ public String createEffectiveConfigContent() { } ProfilerConfiguration profilerConfiguration = ProfilerConfiguration.SUPPLIER.get(); SnapshotProfilingConfiguration snapshotConfiguration = - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.get(); + SnapshotProfilingConfiguration.SUPPLIER.get(); return processModel(model, profilerConfiguration, snapshotConfiguration); } diff --git a/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactory.java b/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactory.java index 687fda85a..358117c22 100644 --- a/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactory.java +++ b/opamp/src/main/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactory.java @@ -18,7 +18,6 @@ import com.splunk.opentelemetry.profiler.ProfilerConfiguration; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; -import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingEnvVarsConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; class EnvVarsEffectiveConfigFileFactory implements EffectiveConfigFactory { @@ -50,7 +49,7 @@ public String createEffectiveConfigContent() { private void addSplunkEnvVars(EffectiveConfigBuilder builder) { ProfilerConfiguration profilerConfiguration = ProfilerConfiguration.SUPPLIER.get(); SnapshotProfilingConfiguration snapshotConfiguration = - new SnapshotProfilingEnvVarsConfiguration(config); + SnapshotProfilingConfiguration.SUPPLIER.get(); builder .add("SPLUNK_PROFILER_ENABLED", profilerConfiguration.isEnabled()) diff --git a/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactoryTest.java b/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactoryTest.java index 091c6bf7a..43ed0936e 100644 --- a/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactoryTest.java +++ b/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/DeclarativeEffectiveConfigFileFactoryTest.java @@ -26,7 +26,7 @@ import com.splunk.opentelemetry.profiler.ProfilerConfiguration; import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfigurationFactory; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; -import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingDeclarativeConfiguration; +import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingDeclarativeConfigurationFactory; import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; @@ -58,7 +58,7 @@ class DeclarativeEffectiveConfigFileFactoryTest { void afterEach() { DeclarativeConfigurationInterceptor.reset(); ProfilerConfiguration.SUPPLIER.reset(); - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.reset(); + SnapshotProfilingConfiguration.SUPPLIER.reset(); } @Test @@ -399,8 +399,8 @@ public static void main(String[] args) throws Exception { getDistributionConfig(model).getStructured("profiling", empty()); ProfilerConfiguration.SUPPLIER.configure( ProfilerDeclarativeConfigurationFactory.create(profilingConfig)); - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.configure( - new SnapshotProfilingDeclarativeConfiguration(profilingConfig)); + SnapshotProfilingConfiguration.SUPPLIER.configure( + SnapshotProfilingDeclarativeConfigurationFactory.create(profilingConfig)); DeclarativeConfigurationBuilder builder = new DeclarativeConfigurationBuilder(); new DeclarativeConfigurationInterceptor().customize(builder); diff --git a/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactoryTest.java b/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactoryTest.java index 5e2c4d578..a83dd1cb1 100644 --- a/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactoryTest.java +++ b/opamp/src/test/java/com/splunk/opentelemetry/opamp/effectiveconfig/EnvVarsEffectiveConfigFileFactoryTest.java @@ -20,6 +20,8 @@ import com.splunk.opentelemetry.profiler.ProfilerConfiguration; import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfigurationFactory; +import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; +import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingEnvVarsConfigurationFactory; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.io.IOException; import java.io.StringReader; @@ -33,6 +35,7 @@ class EnvVarsEffectiveConfigFileFactoryTest { @AfterEach void tearDown() { ProfilerConfiguration.SUPPLIER.reset(); + SnapshotProfilingConfiguration.SUPPLIER.reset(); } @Test @@ -163,6 +166,8 @@ void buildFileContent_usesSignalSpecificProtocolWhenResolvingEndpoints() throws private static Properties createFileContent(Map configMap) throws IOException { DefaultConfigProperties config = DefaultConfigProperties.createFromMap(configMap); ProfilerConfiguration.SUPPLIER.configure(ProfilerEnvVarsConfigurationFactory.create(config)); + SnapshotProfilingConfiguration.SUPPLIER.configure( + SnapshotProfilingEnvVarsConfigurationFactory.create(config)); String fileContent = new EnvVarsEffectiveConfigFileFactory(config).createEffectiveConfigContent(); diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java index 868e9c16f..baf0781cc 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java @@ -16,32 +16,124 @@ package com.splunk.opentelemetry.profiler.snapshot; +import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; import java.time.Duration; +import java.util.Objects; import java.util.logging.Logger; +import javax.annotation.Nullable; -public interface SnapshotProfilingConfiguration { - double MAX_SELECTION_PROBABILITY = 1.0; - double DEFAULT_SELECTION_PROBABILITY = 0.01; - int DEFAULT_STACK_DEPTH = 1024; - long DEFAULT_SAMPLING_INTERVAL = 10; - long DEFAULT_EXPORT_INTERVAL = 5000; - int DEFAULT_STAGING_CAPACITY = 2000; +public class SnapshotProfilingConfiguration { + public static final OptionalConfigurableSupplier SUPPLIER = + new OptionalConfigurableSupplier<>(); - void log(); + private static final Logger logger = + Logger.getLogger(SnapshotProfilingConfiguration.class.getName()); - boolean isEnabled(); + static final double MAX_SELECTION_PROBABILITY = 1.0; + static final double DEFAULT_SELECTION_PROBABILITY = 0.01; + static final int DEFAULT_STACK_DEPTH = 1024; + static final long DEFAULT_SAMPLING_INTERVAL = 10; + static final long DEFAULT_EXPORT_INTERVAL = 5000; + static final int DEFAULT_STAGING_CAPACITY = 2000; - double getSnapshotSelectionProbability(); + private final boolean enabled; + private final double snapshotSelectionProbability; + private final int stackDepth; + private final Duration samplingInterval; + private final Duration exportInterval; + private final int stagingCapacity; + @Nullable private final Object configProperties; - int getStackDepth(); + private SnapshotProfilingConfiguration(Builder builder) { + enabled = builder.enabled; + snapshotSelectionProbability = builder.snapshotSelectionProbability; + stackDepth = builder.stackDepth; + samplingInterval = builder.samplingInterval; + exportInterval = builder.exportInterval; + stagingCapacity = builder.stagingCapacity; + configProperties = builder.configProperties; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder(this); + } + + public void log() { + logger.info("Snapshot Profiler Configuration:"); + logger.info("--------------------------------"); + + log("Enabled", isEnabled()); + log("SelectionProbability", getSnapshotSelectionProbability()); + log("StackDepth", getStackDepth()); + log("SamplingInterval", getSamplingInterval().toMillis() + "ms"); + log("ExportInterval", getExportInterval().toMillis() + "ms"); + log("StagingCapacity", getStagingCapacity()); + + logger.info("--------------------------------"); + } + + public boolean isEnabled() { + return enabled; + } + + public double getSnapshotSelectionProbability() { + return snapshotSelectionProbability; + } + + public int getStackDepth() { + return stackDepth; + } + + public Duration getSamplingInterval() { + return samplingInterval; + } + + public Duration getExportInterval() { + return exportInterval; + } - Duration getSamplingInterval(); + public int getStagingCapacity() { + return stagingCapacity; + } - Duration getExportInterval(); + @Nullable + public Object getConfigProperties() { + return configProperties; + } - int getStagingCapacity(); + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof SnapshotProfilingConfiguration)) { + return false; + } + SnapshotProfilingConfiguration that = (SnapshotProfilingConfiguration) other; + return enabled == that.enabled + && Double.compare(that.snapshotSelectionProbability, snapshotSelectionProbability) == 0 + && stackDepth == that.stackDepth + && stagingCapacity == that.stagingCapacity + && Objects.equals(samplingInterval, that.samplingInterval) + && Objects.equals(exportInterval, that.exportInterval) + && Objects.equals(configProperties, that.configProperties); + } - Object getConfigProperties(); + @Override + public int hashCode() { + return Objects.hash( + enabled, + snapshotSelectionProbability, + stackDepth, + samplingInterval, + exportInterval, + stagingCapacity, + configProperties); + } static double validateSelectionProbability(double selectionProbability, Logger logger) { if (selectionProbability > MAX_SELECTION_PROBABILITY) { @@ -55,7 +147,7 @@ static double validateSelectionProbability(double selectionProbability, Logger l } if (selectionProbability <= 0) { logger.warning( - "Snapshot selection probability must be greater than 0. Using default snapshot" + "Snapshot selection probability must be greater than 0. Using default snapshot " + "selection probability of '" + DEFAULT_SELECTION_PROBABILITY + "' instead."); @@ -63,4 +155,69 @@ static double validateSelectionProbability(double selectionProbability, Logger l } return selectionProbability; } + + private static void log(String key, Object value) { + logger.info(String.format("%24s : %s", key, value)); + } + + public static class Builder { + private boolean enabled; + private double snapshotSelectionProbability = DEFAULT_SELECTION_PROBABILITY; + private int stackDepth = DEFAULT_STACK_DEPTH; + private Duration samplingInterval = Duration.ofMillis(DEFAULT_SAMPLING_INTERVAL); + private Duration exportInterval = Duration.ofMillis(DEFAULT_EXPORT_INTERVAL); + private int stagingCapacity = DEFAULT_STAGING_CAPACITY; + @Nullable private Object configProperties; + + private Builder() {} + + private Builder(SnapshotProfilingConfiguration config) { + enabled = config.enabled; + snapshotSelectionProbability = config.snapshotSelectionProbability; + stackDepth = config.stackDepth; + samplingInterval = config.samplingInterval; + exportInterval = config.exportInterval; + stagingCapacity = config.stagingCapacity; + configProperties = config.configProperties; + } + + public SnapshotProfilingConfiguration build() { + return new SnapshotProfilingConfiguration(this); + } + + public Builder setEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder setSnapshotSelectionProbability(double snapshotSelectionProbability) { + this.snapshotSelectionProbability = snapshotSelectionProbability; + return this; + } + + public Builder setStackDepth(int stackDepth) { + this.stackDepth = stackDepth; + return this; + } + + public Builder setSamplingInterval(Duration samplingInterval) { + this.samplingInterval = Objects.requireNonNull(samplingInterval); + return this; + } + + public Builder setExportInterval(Duration exportInterval) { + this.exportInterval = Objects.requireNonNull(exportInterval); + return this; + } + + public Builder setStagingCapacity(int stagingCapacity) { + this.stagingCapacity = stagingCapacity; + return this; + } + + public Builder setConfigProperties(@Nullable Object configProperties) { + this.configProperties = configProperties; + return this; + } + } } 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 2374e0b32..84d6d737b 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 @@ -46,8 +46,8 @@ public void customize(DeclarativeConfigurationCustomizer configurationCustomizer @VisibleForTesting OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel model) { - SnapshotProfilingDeclarativeConfiguration snapshotProfiling = getSnapshotProfilingConfig(model); - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.configure(snapshotProfiling); + SnapshotProfilingConfiguration snapshotProfiling = getSnapshotProfilingConfig(model); + SnapshotProfilingConfiguration.SUPPLIER.configure(snapshotProfiling); if (snapshotProfiling.isEnabled()) { initActiveSpansTracking(); @@ -58,8 +58,7 @@ OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel m return model; } - private void initStackTraceSampler( - SnapshotProfilingDeclarativeConfiguration snapshotProfilingConfig) { + private void initStackTraceSampler(SnapshotProfilingConfiguration snapshotProfilingConfig) { StackTraceSamplerInitializer.setupStackTraceSampler(snapshotProfilingConfig); } @@ -87,11 +86,11 @@ private void initActiveSpansTracking() { contextStorageWrapper.wrapContextStorage(registry); } - private static SnapshotProfilingDeclarativeConfiguration getSnapshotProfilingConfig( + private static SnapshotProfilingConfiguration getSnapshotProfilingConfig( OpenTelemetryConfigurationModel model) { DeclarativeConfigProperties profilingConfig = getDistributionConfig(model).getStructured("profiling", empty()); - return new SnapshotProfilingDeclarativeConfiguration(profilingConfig); + return SnapshotProfilingDeclarativeConfigurationFactory.create(profilingConfig); } @VisibleForTesting diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfiguration.java deleted file mode 100644 index 46b6f9640..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfiguration.java +++ /dev/null @@ -1,104 +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 static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; - -import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import java.time.Duration; -import java.util.logging.Logger; - -public class SnapshotProfilingDeclarativeConfiguration implements SnapshotProfilingConfiguration { - public static final OptionalConfigurableSupplier - SUPPLIER = new OptionalConfigurableSupplier<>(); - - private static final Logger logger = - Logger.getLogger(SnapshotProfilingDeclarativeConfiguration.class.getName()); - private final DeclarativeConfigProperties profilingConfig; - - private static final String ROOT_NODE_NAME = "callgraphs"; - - public SnapshotProfilingDeclarativeConfiguration(DeclarativeConfigProperties profilingConfig) { - this.profilingConfig = profilingConfig; - } - - @Override - public void log() { - logger.info("Snapshot Profiler Configuration:"); - logger.info("--------------------------------"); - - log("Enabled", isEnabled()); - log("SelectionProbability", getSnapshotSelectionProbability()); - log("StackDepth", getStackDepth()); - log("SamplingInterval", getSamplingInterval().toMillis() + "ms"); - log("ExportInterval", getExportInterval().toMillis() + "ms"); - log("StagingCapacity", getStagingCapacity()); - - logger.info("--------------------------------"); - } - - @Override - public boolean isEnabled() { - return (profilingConfig != null) && profilingConfig.getPropertyKeys().contains(ROOT_NODE_NAME); - } - - @Override - public double getSnapshotSelectionProbability() { - double selectionProbability = - getConfigRoot().getDouble("selection_probability", DEFAULT_SELECTION_PROBABILITY); - return SnapshotProfilingConfiguration.validateSelectionProbability( - selectionProbability, logger); - } - - @Override - public int getStackDepth() { - return getConfigRoot().getInt("stack_depth", DEFAULT_STACK_DEPTH); - } - - @Override - public Duration getSamplingInterval() { - return getDuration("sampling_interval", DEFAULT_SAMPLING_INTERVAL); - } - - @Override - public Duration getExportInterval() { - return getDuration("export_interval", DEFAULT_EXPORT_INTERVAL); - } - - @Override - public int getStagingCapacity() { - return getConfigRoot().getInt("staging_capacity", DEFAULT_STAGING_CAPACITY); - } - - @Override - public Object getConfigProperties() { - return profilingConfig; - } - - private DeclarativeConfigProperties getConfigRoot() { - return profilingConfig.getStructured(ROOT_NODE_NAME, empty()); - } - - private void log(String key, Object value) { - logger.info(String.format("%24s : %s", key, value)); - } - - private Duration getDuration(String key, long defaultValue) { - return Duration.ofMillis(getConfigRoot().getLong(key, defaultValue)); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactory.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactory.java new file mode 100644 index 000000000..5d09b0725 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactory.java @@ -0,0 +1,75 @@ +/* + * 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 static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import java.time.Duration; +import java.util.logging.Logger; + +public final class SnapshotProfilingDeclarativeConfigurationFactory { + private static final Logger logger = + Logger.getLogger(SnapshotProfilingDeclarativeConfigurationFactory.class.getName()); + + private static final String ROOT_NODE_NAME = "callgraphs"; + + private SnapshotProfilingDeclarativeConfigurationFactory() {} + + public static SnapshotProfilingConfiguration create(DeclarativeConfigProperties profilingConfig) { + DeclarativeConfigProperties config = profilingConfig == null ? empty() : profilingConfig; + DeclarativeConfigProperties configRoot = getConfigRoot(config); + + return SnapshotProfilingConfiguration.builder() + .setEnabled(config.getPropertyKeys().contains(ROOT_NODE_NAME)) + .setSnapshotSelectionProbability(getSnapshotSelectionProbability(configRoot)) + .setStackDepth( + configRoot.getInt("stack_depth", SnapshotProfilingConfiguration.DEFAULT_STACK_DEPTH)) + .setSamplingInterval( + getDuration( + configRoot, + "sampling_interval", + SnapshotProfilingConfiguration.DEFAULT_SAMPLING_INTERVAL)) + .setExportInterval( + getDuration( + configRoot, + "export_interval", + SnapshotProfilingConfiguration.DEFAULT_EXPORT_INTERVAL)) + .setStagingCapacity( + configRoot.getInt( + "staging_capacity", SnapshotProfilingConfiguration.DEFAULT_STAGING_CAPACITY)) + .setConfigProperties(config) + .build(); + } + + private static DeclarativeConfigProperties getConfigRoot(DeclarativeConfigProperties config) { + return config.getStructured(ROOT_NODE_NAME, empty()); + } + + private static double getSnapshotSelectionProbability(DeclarativeConfigProperties configRoot) { + double selectionProbability = + configRoot.getDouble( + "selection_probability", SnapshotProfilingConfiguration.DEFAULT_SELECTION_PROBABILITY); + return SnapshotProfilingConfiguration.validateSelectionProbability( + selectionProbability, logger); + } + + private static Duration getDuration( + DeclarativeConfigProperties config, String key, long defaultValue) { + return Duration.ofMillis(config.getLong(key, defaultValue)); + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfiguration.java deleted file mode 100644 index ca87acee8..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfiguration.java +++ /dev/null @@ -1,114 +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 io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.time.Duration; -import java.util.logging.Logger; - -public class SnapshotProfilingEnvVarsConfiguration implements SnapshotProfilingConfiguration { - private static final Logger logger = - Logger.getLogger(SnapshotProfilingEnvVarsConfiguration.class.getName()); - - private final ConfigProperties properties; - - // Visible for tests - public static final String CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER = - "splunk.snapshot.profiler.enabled"; - public static final String SELECTION_PROBABILITY_KEY = "splunk.snapshot.selection.probability"; - public static final String STACK_DEPTH_KEY = "splunk.snapshot.profiler.max.stack.depth"; - public static final String SAMPLING_INTERVAL_KEY = "splunk.snapshot.sampling.interval"; - public static final String EXPORT_INTERVAL_KEY = "splunk.snapshot.profiler.export.interval"; - public static final String STAGING_CAPACITY_KEY = "splunk.snapshot.profiler.staging.capacity"; - - public SnapshotProfilingEnvVarsConfiguration(ConfigProperties properties) { - this.properties = properties; - } - - @Override - public void log() { - logger.fine("Snapshot Profiler Configuration:"); - logger.fine("-------------------------------------------------------"); - - log(CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, isEnabled()); - log(SELECTION_PROBABILITY_KEY, getSnapshotSelectionProbability()); - log(STACK_DEPTH_KEY, getStackDepth()); - log(SAMPLING_INTERVAL_KEY, getSamplingInterval().toMillis() + "ms"); - log(EXPORT_INTERVAL_KEY, getExportInterval().toMillis() + "ms"); - log(STAGING_CAPACITY_KEY, getStagingCapacity()); - logger.fine("-------------------------------------------------------"); - } - - @Override - public boolean isEnabled() { - return properties.getBoolean(CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, false); - } - - @Override - public double getSnapshotSelectionProbability() { - String selectionProbabilityPropertyValue = - properties.getString( - SELECTION_PROBABILITY_KEY, String.valueOf(DEFAULT_SELECTION_PROBABILITY)); - try { - double selectionProbability = Double.parseDouble(selectionProbabilityPropertyValue); - return SnapshotProfilingConfiguration.validateSelectionProbability( - selectionProbability, logger); - } catch (NumberFormatException e) { - logger.warning( - "Invalid snapshot selection probability: '" - + selectionProbabilityPropertyValue - + "', using default probability of '" - + DEFAULT_SELECTION_PROBABILITY - + "'"); - return DEFAULT_SELECTION_PROBABILITY; - } - } - - @Override - public int getStackDepth() { - return properties.getInt(STACK_DEPTH_KEY, DEFAULT_STACK_DEPTH); - } - - @Override - public Duration getSamplingInterval() { - return properties.getDuration( - SAMPLING_INTERVAL_KEY, Duration.ofMillis(DEFAULT_SAMPLING_INTERVAL)); - } - - @Override - public Duration getExportInterval() { - return properties.getDuration(EXPORT_INTERVAL_KEY, Duration.ofMillis(DEFAULT_EXPORT_INTERVAL)); - } - - @Override - public int getStagingCapacity() { - return properties.getInt(STAGING_CAPACITY_KEY, DEFAULT_STAGING_CAPACITY); - } - - @Override - public Object getConfigProperties() { - return properties; - } - - private void log(String key, Object value) { - logger.fine(" " + pad(key) + " : " + value); - } - - private String pad(String str) { - return String.format("%42s", str); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactory.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactory.java new file mode 100644 index 000000000..a1b6136d7 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactory.java @@ -0,0 +1,77 @@ +/* + * 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 io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.time.Duration; +import java.util.logging.Logger; + +public final class SnapshotProfilingEnvVarsConfigurationFactory { + private static final Logger logger = + Logger.getLogger(SnapshotProfilingEnvVarsConfigurationFactory.class.getName()); + + // Visible for tests + static final String CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER = "splunk.snapshot.profiler.enabled"; + static final String SELECTION_PROBABILITY_KEY = "splunk.snapshot.selection.probability"; + static final String STACK_DEPTH_KEY = "splunk.snapshot.profiler.max.stack.depth"; + static final String SAMPLING_INTERVAL_KEY = "splunk.snapshot.sampling.interval"; + static final String EXPORT_INTERVAL_KEY = "splunk.snapshot.profiler.export.interval"; + static final String STAGING_CAPACITY_KEY = "splunk.snapshot.profiler.staging.capacity"; + + private SnapshotProfilingEnvVarsConfigurationFactory() {} + + public static SnapshotProfilingConfiguration create(ConfigProperties properties) { + return SnapshotProfilingConfiguration.builder() + .setEnabled(properties.getBoolean(CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, false)) + .setSnapshotSelectionProbability(getSnapshotSelectionProbability(properties)) + .setStackDepth( + properties.getInt(STACK_DEPTH_KEY, SnapshotProfilingConfiguration.DEFAULT_STACK_DEPTH)) + .setSamplingInterval( + properties.getDuration( + SAMPLING_INTERVAL_KEY, + Duration.ofMillis(SnapshotProfilingConfiguration.DEFAULT_SAMPLING_INTERVAL))) + .setExportInterval( + properties.getDuration( + EXPORT_INTERVAL_KEY, + Duration.ofMillis(SnapshotProfilingConfiguration.DEFAULT_EXPORT_INTERVAL))) + .setStagingCapacity( + properties.getInt( + STAGING_CAPACITY_KEY, SnapshotProfilingConfiguration.DEFAULT_STAGING_CAPACITY)) + .setConfigProperties(properties) + .build(); + } + + private static double getSnapshotSelectionProbability(ConfigProperties properties) { + String selectionProbabilityPropertyValue = + properties.getString( + SELECTION_PROBABILITY_KEY, + String.valueOf(SnapshotProfilingConfiguration.DEFAULT_SELECTION_PROBABILITY)); + try { + double selectionProbability = Double.parseDouble(selectionProbabilityPropertyValue); + return SnapshotProfilingConfiguration.validateSelectionProbability( + selectionProbability, logger); + } catch (NumberFormatException e) { + logger.warning( + "Invalid snapshot selection probability: '" + + selectionProbabilityPropertyValue + + "', using default probability of '" + + SnapshotProfilingConfiguration.DEFAULT_SELECTION_PROBABILITY + + "'"); + return SnapshotProfilingConfiguration.DEFAULT_SELECTION_PROBABILITY; + } + } +} 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 e1a8b4a8c..8bfcd76fa 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 @@ -44,8 +44,7 @@ public SnapshotProfilingSdkCustomizer() { private static Function stackTraceSamplerProvider() { return properties -> { - SnapshotProfilingEnvVarsConfiguration configuration = - new SnapshotProfilingEnvVarsConfiguration(properties); + SnapshotProfilingConfiguration configuration = SnapshotProfilingConfiguration.SUPPLIER.get(); Duration samplingPeriod = configuration.getSamplingInterval(); StagingArea.SUPPLIER.configure(createStagingArea(configuration)); return new PeriodicStackTraceSampler( @@ -53,8 +52,7 @@ private static Function stackTraceSamplerPr }; } - private static StagingArea createStagingArea( - SnapshotProfilingEnvVarsConfiguration configuration) { + private static StagingArea createStagingArea(SnapshotProfilingConfiguration configuration) { Duration interval = configuration.getExportInterval(); int capacity = configuration.getStagingCapacity(); return new PeriodicallyExportingStagingArea(StackTraceExporter.SUPPLIER, interval, capacity); @@ -79,6 +77,9 @@ private SnapshotProfilingSdkCustomizer( @Override public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { + // initializeSnapshotProfilingConfiguration must be executed first + autoConfigurationCustomizer.addPropertiesCustomizer(initializeSnapshotProfilingConfiguration()); + autoConfigurationCustomizer .addTracerProviderCustomizer(snapshotProfilingSpanProcessor(registry)) .addPropertiesCustomizer(setupStackTraceSampler()) @@ -86,10 +87,19 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { .addTracerProviderCustomizer(addShutdownHook()); } + private Function> + initializeSnapshotProfilingConfiguration() { + return properties -> { + SnapshotProfilingConfiguration.SUPPLIER.configure( + SnapshotProfilingEnvVarsConfigurationFactory.create(properties)); + return Collections.emptyMap(); + }; + } + private BiFunction addShutdownHook() { return (builder, properties) -> { - if (snapshotProfilingEnabled(properties)) { + if (snapshotProfilingEnabled()) { builder.addSpanProcessor(new SdkShutdownHook()); } return builder; @@ -99,9 +109,9 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { private BiFunction snapshotProfilingSpanProcessor(TraceRegistry registry) { return (builder, properties) -> { - if (snapshotProfilingEnabled(properties)) { + if (snapshotProfilingEnabled()) { double selectionProbability = - new SnapshotProfilingEnvVarsConfiguration(properties).getSnapshotSelectionProbability(); + SnapshotProfilingConfiguration.SUPPLIER.get().getSnapshotSelectionProbability(); return builder.addSpanProcessor( new SnapshotProfilingSpanProcessor( @@ -117,7 +127,7 @@ private boolean includeTraceContextPropagator(Set configuredPropagators) private Function> setupStackTraceSampler() { return properties -> { - if (snapshotProfilingEnabled(properties)) { + if (snapshotProfilingEnabled()) { StackTraceSampler sampler = samplerProvider.apply(properties); ConfigurableSupplier supplier = StackTraceSampler.SUPPLIER; supplier.configure(sampler); @@ -129,14 +139,14 @@ private Function> setupStackTraceSampler() private Function> startTrackingActiveSpans( TraceRegistry registry) { return properties -> { - if (snapshotProfilingEnabled(properties)) { + if (snapshotProfilingEnabled()) { contextStorageWrapper.wrapContextStorage(registry); } return Collections.emptyMap(); }; } - private boolean snapshotProfilingEnabled(ConfigProperties properties) { - return new SnapshotProfilingEnvVarsConfiguration(properties).isEnabled(); + private boolean snapshotProfilingEnabled() { + return SnapshotProfilingConfiguration.SUPPLIER.get().isEnabled(); } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSpanProcessorComponentProvider.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSpanProcessorComponentProvider.java index a0b0d0b8a..f76757f71 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSpanProcessorComponentProvider.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSpanProcessorComponentProvider.java @@ -48,8 +48,7 @@ public String getName() { public SnapshotProfilingSpanProcessor create( DeclarativeConfigProperties declarativeConfigProperties) { double selectionProbability = - new SnapshotProfilingDeclarativeConfiguration(declarativeConfigProperties) - .getSnapshotSelectionProbability(); + SnapshotProfilingConfiguration.SUPPLIER.get().getSnapshotSelectionProbability(); return new SnapshotProfilingSpanProcessor( traceRegistry, new TraceIdBasedSnapshotSelector(selectionProbability)); } 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 index 226831cba..1a0bd3770 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java @@ -16,8 +16,6 @@ package com.splunk.opentelemetry.profiler.snapshot; -import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; - import com.google.auto.service.AutoService; import com.google.common.annotations.VisibleForTesting; import com.splunk.opentelemetry.profiler.OtelLoggerFactory; @@ -26,8 +24,10 @@ import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; +import java.util.Collections; @AutoService(AgentListener.class) public class StackTraceExporterActivator implements AgentListener { @@ -47,7 +47,7 @@ public StackTraceExporterActivator() { @Override public void afterAgent(AutoConfiguredOpenTelemetrySdk sdk) { - SnapshotProfilingConfiguration configuration = getSnapshotProfilingConfiguration(sdk); + SnapshotProfilingConfiguration configuration = SnapshotProfilingConfiguration.SUPPLIER.get(); if (!configuration.isEnabled()) { return; } @@ -66,7 +66,7 @@ private Logger buildLogger(AutoConfiguredOpenTelemetrySdk sdk, Object configProp Resource resource = AutoConfigureUtil.getResource(sdk); if (configProperties instanceof DeclarativeConfigProperties) { DeclarativeConfigProperties exporterConfig = - ((DeclarativeConfigProperties) configProperties).getStructured("exporter", empty()); + getExporterConfig((DeclarativeConfigProperties) configProperties); return otelLoggerFactory.build(exporterConfig, resource); } if (configProperties instanceof ConfigProperties) { @@ -76,13 +76,15 @@ private Logger buildLogger(AutoConfiguredOpenTelemetrySdk sdk, Object configProp "Unsupported config properties type: " + configProperties.getClass().getName()); } - private static SnapshotProfilingConfiguration getSnapshotProfilingConfiguration( - AutoConfiguredOpenTelemetrySdk sdk) { - if (SnapshotProfilingDeclarativeConfiguration.SUPPLIER.isConfigured()) { - return SnapshotProfilingDeclarativeConfiguration.SUPPLIER.get(); - } else { - ConfigProperties configProperties = AutoConfigureUtil.getConfig(sdk); - return new SnapshotProfilingEnvVarsConfiguration(configProperties); + private static DeclarativeConfigProperties getExporterConfig( + DeclarativeConfigProperties configProperties) { + DeclarativeConfigProperties exporterConfig = configProperties.getStructured("exporter"); + if (exporterConfig != null) { + return exporterConfig; } + // DeclarativeConfigProperties.empty() would drop the component loader that discovers OTLP + // sender providers. + return YamlDeclarativeConfigProperties.create( + Collections.emptyMap(), configProperties.getComponentLoader()); } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java index 3d7d28fe2..2db8bc81a 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java @@ -22,7 +22,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingDeclarativeConfiguration; +import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import java.io.IOException; @@ -44,7 +44,7 @@ class JfrAgentListenerTest { @AfterEach void resetDeclarativeConfigSuppliers() { ProfilerConfiguration.SUPPLIER.reset(); - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.reset(); + SnapshotProfilingConfiguration.SUPPLIER.reset(); } @Test 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 dc3374189..ecb07ab81 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 @@ -40,7 +40,7 @@ class SnapshotProfilingConfigurationCustomizerProviderTest { @AfterEach void resetSuppliers() { - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.reset(); + SnapshotProfilingConfiguration.SUPPLIER.reset(); StackTraceSampler.SUPPLIER.reset(); StagingArea.SUPPLIER.reset(); SpanTracker.SUPPLIER.reset(); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationTest.java new file mode 100644 index 000000000..60c7d9bcf --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfigurationTest.java @@ -0,0 +1,83 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import org.junit.jupiter.api.Test; + +class SnapshotProfilingConfigurationTest { + + @Test + void toBuilder_shouldCopyExistingConfigurationWithoutMutation() { + Object configProperties = new Object(); + SnapshotProfilingConfiguration original = + SnapshotProfilingConfiguration.builder() + .setEnabled(true) + .setSnapshotSelectionProbability(0.25) + .setStackDepth(73) + .setSamplingInterval(Duration.ofMillis(1410)) + .setExportInterval(Duration.ofSeconds(30)) + .setStagingCapacity(321) + .setConfigProperties(configProperties) + .build(); + + SnapshotProfilingConfiguration copy = original.toBuilder().build(); + + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + assertThat(copy.getConfigProperties()).isSameAs(configProperties); + } + + @Test + void toBuilder_shouldCopyExistingConfigurationAndAllowMutation() { + Object configProperties = new Object(); + Object mutatedConfigProperties = new Object(); + SnapshotProfilingConfiguration original = + SnapshotProfilingConfiguration.builder() + .setEnabled(false) + .setSnapshotSelectionProbability(0.25) + .setStackDepth(73) + .setSamplingInterval(Duration.ofMillis(1410)) + .setExportInterval(Duration.ofSeconds(30)) + .setStagingCapacity(321) + .setConfigProperties(configProperties) + .build(); + + SnapshotProfilingConfiguration copy = + original.toBuilder() + .setEnabled(true) + .setSnapshotSelectionProbability(0.5) + .setStackDepth(142) + .setSamplingInterval(Duration.ofMillis(2500)) + .setExportInterval(Duration.ofSeconds(60)) + .setStagingCapacity(654) + .setConfigProperties(mutatedConfigProperties) + .build(); + + assertThat(copy).isNotEqualTo(original); + assertThat(copy.isEnabled()).isTrue(); + assertThat(copy.getSnapshotSelectionProbability()).isEqualTo(0.5); + assertThat(copy.getStackDepth()).isEqualTo(142); + assertThat(copy.getSamplingInterval()).isEqualTo(Duration.ofMillis(2500)); + assertThat(copy.getExportInterval()).isEqualTo(Duration.ofSeconds(60)); + assertThat(copy.getStagingCapacity()).isEqualTo(654); + assertThat(copy.getConfigProperties()).isSameAs(mutatedConfigProperties); + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactoryTest.java similarity index 87% rename from profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationTest.java rename to profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactoryTest.java index 7c58b4ff0..afcf10f9b 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingDeclarativeConfigurationFactoryTest.java @@ -25,7 +25,7 @@ import java.time.Duration; import org.junit.jupiter.api.Test; -class SnapshotProfilingDeclarativeConfigurationTest { +class SnapshotProfilingDeclarativeConfigurationFactoryTest { @Test void shouldMapYamlToConfiguration() { // given @@ -47,8 +47,8 @@ void shouldMapYamlToConfiguration() { DeclarativeConfigProperties profilingConfig = getProfilingConfig(model); // when - SnapshotProfilingDeclarativeConfiguration config = - new SnapshotProfilingDeclarativeConfiguration(profilingConfig); + SnapshotProfilingConfiguration config = + SnapshotProfilingDeclarativeConfigurationFactory.create(profilingConfig); // then assertThat(config.isEnabled()).isTrue(); @@ -75,8 +75,8 @@ void shouldEnableSnapshotProfilingWithDefaults() { DeclarativeConfigProperties snapshotProfilingConfig = getProfilingConfig(model); // when - SnapshotProfilingDeclarativeConfiguration config = - new SnapshotProfilingDeclarativeConfiguration(snapshotProfilingConfig); + SnapshotProfilingConfiguration config = + SnapshotProfilingDeclarativeConfigurationFactory.create(snapshotProfilingConfig); // then assertThat(config.isEnabled()).isTrue(); @@ -102,8 +102,8 @@ void shouldDisableSnapshotProfiling() { DeclarativeConfigProperties profilingConfig = getProfilingConfig(model); // when - SnapshotProfilingDeclarativeConfiguration config = - new SnapshotProfilingDeclarativeConfiguration(profilingConfig); + SnapshotProfilingConfiguration config = + SnapshotProfilingDeclarativeConfigurationFactory.create(profilingConfig); // then assertThat(config.isEnabled()).isFalse(); @@ -126,8 +126,8 @@ void shouldReturnValidSelectionInterval_aboveMax() { DeclarativeConfigProperties profilingConfig = getProfilingConfig(model); // when - SnapshotProfilingDeclarativeConfiguration config = - new SnapshotProfilingDeclarativeConfiguration(profilingConfig); + SnapshotProfilingConfiguration config = + SnapshotProfilingDeclarativeConfigurationFactory.create(profilingConfig); // then assertThat(config.getSnapshotSelectionProbability()) @@ -151,8 +151,8 @@ void shouldReturnValidSelectionInterval_tooLow() { DeclarativeConfigProperties profilingConfig = getProfilingConfig(model); // when - SnapshotProfilingDeclarativeConfiguration config = - new SnapshotProfilingDeclarativeConfiguration(profilingConfig); + SnapshotProfilingConfiguration config = + SnapshotProfilingDeclarativeConfigurationFactory.create(profilingConfig); // then assertThat(config.isEnabled()).isTrue(); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactoryTest.java similarity index 66% rename from profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationTest.java rename to profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactoryTest.java index 1463c4ec4..f741e65d3 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingEnvVarsConfigurationFactoryTest.java @@ -26,11 +26,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -class SnapshotProfilingEnvVarsConfigurationTest { +class SnapshotProfilingEnvVarsConfigurationFactoryTest { private static final ComponentLoader COMPONENT_LOADER = ComponentLoader.forClassLoader( - SnapshotProfilingEnvVarsConfigurationTest.class.getClassLoader()); + SnapshotProfilingEnvVarsConfigurationFactoryTest.class.getClassLoader()); @ParameterizedTest @ValueSource(booleans = {true, false}) @@ -39,10 +39,10 @@ void shouldSupportEnabledAndDisabledSnapshotProfiler(boolean enabled) { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, + SnapshotProfilingEnvVarsConfigurationFactory.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, String.valueOf(enabled)), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when boolean actual = configuration.isEnabled(); @@ -58,10 +58,10 @@ void shouldReturnValidSelectionProbability(double selectionRate) { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.SELECTION_PROBABILITY_KEY, + SnapshotProfilingEnvVarsConfigurationFactory.SELECTION_PROBABILITY_KEY, String.valueOf(selectionRate)), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when double actualSelectionRate = configuration.getSnapshotSelectionProbability(); @@ -77,10 +77,10 @@ void shouldReturnMaxSelectionProbabilityWhenAboveMax(double selectionRate) { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.SELECTION_PROBABILITY_KEY, + SnapshotProfilingEnvVarsConfigurationFactory.SELECTION_PROBABILITY_KEY, String.valueOf(selectionRate)), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when double actualSelectionRate = configuration.getSnapshotSelectionProbability(); @@ -94,9 +94,9 @@ void shouldFallbackSelectionProbabilityToDefaultWhenZero() { // given var properties = DefaultConfigProperties.create( - Map.of(SnapshotProfilingEnvVarsConfiguration.SELECTION_PROBABILITY_KEY, "0"), + Map.of(SnapshotProfilingEnvVarsConfigurationFactory.SELECTION_PROBABILITY_KEY, "0"), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when double actualSelectionRate = configuration.getSnapshotSelectionProbability(); @@ -110,9 +110,11 @@ void shouldReturnDefaultSelectionProbabilityWhenNotANumber() { // given var properties = DefaultConfigProperties.create( - Map.of(SnapshotProfilingEnvVarsConfiguration.SELECTION_PROBABILITY_KEY, "not-a-number"), + Map.of( + SnapshotProfilingEnvVarsConfigurationFactory.SELECTION_PROBABILITY_KEY, + "not-a-number"), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when double actualSelectionRate = configuration.getSnapshotSelectionProbability(); @@ -127,9 +129,11 @@ void shouldReturnStackDepth() { int depth = 123; var properties = DefaultConfigProperties.create( - Map.of(SnapshotProfilingEnvVarsConfiguration.STACK_DEPTH_KEY, String.valueOf(depth)), + Map.of( + SnapshotProfilingEnvVarsConfigurationFactory.STACK_DEPTH_KEY, + String.valueOf(depth)), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when int actualDepth = configuration.getStackDepth(); @@ -145,10 +149,10 @@ void shouldReturnSamplingInterval() { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.SAMPLING_INTERVAL_KEY, + SnapshotProfilingEnvVarsConfigurationFactory.SAMPLING_INTERVAL_KEY, samplingInterval + "ms"), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when Duration actual = configuration.getSamplingInterval(); @@ -164,9 +168,10 @@ void shouldReturnExportInterval() { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.EXPORT_INTERVAL_KEY, exportInterval + "ms"), + SnapshotProfilingEnvVarsConfigurationFactory.EXPORT_INTERVAL_KEY, + exportInterval + "ms"), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when Duration actual = configuration.getExportInterval(); @@ -182,10 +187,10 @@ void shouldReturnStagingCapacity() { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.STAGING_CAPACITY_KEY, + SnapshotProfilingEnvVarsConfigurationFactory.STAGING_CAPACITY_KEY, String.valueOf(capacity)), COMPONENT_LOADER); - var configuration = new SnapshotProfilingEnvVarsConfiguration(properties); + var configuration = SnapshotProfilingEnvVarsConfigurationFactory.create(properties); // when int actual = configuration.getStagingCapacity(); @@ -204,13 +209,13 @@ void isSnapshotProfilingEnabled(boolean enabled) { DeclarativeConfigPropertiesBridgeBuilder builder = new DeclarativeConfigPropertiesBridgeBuilder(); builder.addOverride( - SnapshotProfilingEnvVarsConfiguration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, enabled); + SnapshotProfilingEnvVarsConfigurationFactory.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, enabled); // when var properties = builder.buildFromInstrumentationConfig(null); // then - assertThat(new SnapshotProfilingEnvVarsConfiguration(properties).isSnapshotProfilingEnabled()) + assertThat(SnapshotProfilingEnvVarsConfigurationFactory.create(properties).isSnapshotProfilingEnabled()) .isEqualTo(enabled); } @@ -220,13 +225,13 @@ void getConfiguredSnapshotProfilerStackDepth(int depth) { // given DeclarativeConfigPropertiesBridgeBuilder builder = new DeclarativeConfigPropertiesBridgeBuilder(); - builder.addOverride(SnapshotProfilingEnvVarsConfiguration.STACK_DEPTH_KEY, depth); + builder.addOverride(SnapshotProfilingEnvVarsConfigurationFactory.STACK_DEPTH_KEY, depth); // when var properties = builder.buildFromInstrumentationConfig(null); // then - assertThat(new SnapshotProfilingEnvVarsConfiguration(properties).getStackDepth()) + assertThat(SnapshotProfilingEnvVarsConfigurationFactory.create(properties).getStackDepth()) .isEqualTo(depth); } @@ -237,13 +242,13 @@ void getConfiguredSnapshotProfilerSamplingInterval(long milliseconds) { DeclarativeConfigPropertiesBridgeBuilder builder = new DeclarativeConfigPropertiesBridgeBuilder(); builder.addOverride( - SnapshotProfilingEnvVarsConfiguration.SAMPLING_INTERVAL_KEY, milliseconds); + SnapshotProfilingEnvVarsConfigurationFactory.SAMPLING_INTERVAL_KEY, milliseconds); // when var properties = builder.buildFromInstrumentationConfig(null); // then - assertThat(new SnapshotProfilingEnvVarsConfiguration(properties).getSamplingInterval()) + assertThat(SnapshotProfilingEnvVarsConfigurationFactory.create(properties).getSamplingInterval()) .isEqualTo(Duration.ofMillis(milliseconds)); } @@ -253,13 +258,13 @@ void getConfiguredSnapshotProfilerEmptyStagingInterval(long milliseconds) { // given DeclarativeConfigPropertiesBridgeBuilder builder = new DeclarativeConfigPropertiesBridgeBuilder(); - builder.addOverride(SnapshotProfilingEnvVarsConfiguration.EXPORT_INTERVAL_KEY, milliseconds); + builder.addOverride(SnapshotProfilingEnvVarsConfigurationFactory.EXPORT_INTERVAL_KEY, milliseconds); // when var properties = builder.buildFromInstrumentationConfig(null); // then - assertThat(new SnapshotProfilingEnvVarsConfiguration(properties).getExportInterval()) + assertThat(SnapshotProfilingEnvVarsConfigurationFactory.create(properties).getExportInterval()) .isEqualTo(Duration.ofMillis(milliseconds)); } @@ -269,13 +274,13 @@ void getConfiguredSnapshotProfilerStagingCapacity(int value) { // given DeclarativeConfigPropertiesBridgeBuilder builder = new DeclarativeConfigPropertiesBridgeBuilder(); - builder.addOverride(SnapshotProfilingEnvVarsConfiguration.STAGING_CAPACITY_KEY, value); + builder.addOverride(SnapshotProfilingEnvVarsConfigurationFactory.STAGING_CAPACITY_KEY, value); // when var properties = builder.buildFromInstrumentationConfig(null); // then - assertThat(new SnapshotProfilingEnvVarsConfiguration(properties).getStagingCapacity()) + assertThat(SnapshotProfilingEnvVarsConfigurationFactory.create(properties).getStagingCapacity()) .isEqualTo(value); } } @@ -285,7 +290,7 @@ class LoggingTest { @RegisterExtension private final LogCapturer log = LogCapturer.create() - .captureForType(SnapshotProfilingEnvVarsConfiguration.class, Level.DEBUG); + .captureForType(SnapshotProfilingEnvVarsConfigurationFactory.class, Level.DEBUG); @Test void includeSnapshotProfilingHeading() { @@ -293,7 +298,7 @@ void includeSnapshotProfilingHeading() { var properties = DefaultConfigProperties.create(Collections.emptyMap(), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then log.assertContains("-----------------------"); @@ -307,14 +312,14 @@ void includeSnapshotProfilingEnabled(boolean enabled) { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, + SnapshotProfilingEnvVarsConfigurationFactory.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, String.valueOf(enabled)), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then log.assertContains( - SnapshotProfilingEnvVarsConfiguration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER + SnapshotProfilingEnvVarsConfigurationFactory.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER + " : " + enabled); } @@ -326,14 +331,14 @@ void includeSnapshotProfilingSelectionRate(double rate) { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.SELECTION_PROBABILITY_KEY, + SnapshotProfilingEnvVarsConfigurationFactory.SELECTION_PROBABILITY_KEY, String.valueOf(rate)), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then log.assertContains( - SnapshotProfilingEnvVarsConfiguration.SELECTION_PROBABILITY_KEY + " : " + rate); + SnapshotProfilingEnvVarsConfigurationFactory.SELECTION_PROBABILITY_KEY + " : " + rate); } @ParameterizedTest @@ -342,12 +347,12 @@ void includeSnapshotProfilingStackTraceDepth(int depth) { // given var properties = DefaultConfigProperties.create( - Map.of(SnapshotProfilingEnvVarsConfiguration.STACK_DEPTH_KEY, String.valueOf(depth)), + Map.of(SnapshotProfilingEnvVarsConfigurationFactory.STACK_DEPTH_KEY, String.valueOf(depth)), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then - log.assertContains(SnapshotProfilingEnvVarsConfiguration.STACK_DEPTH_KEY + " : " + depth); + log.assertContains(SnapshotProfilingEnvVarsConfigurationFactory.STACK_DEPTH_KEY + " : " + depth); } @ParameterizedTest @@ -356,17 +361,17 @@ void includeSnapshotProfilingSamplingInterval(String interval) { // given var properties = DefaultConfigProperties.create( - Map.of(SnapshotProfilingEnvVarsConfiguration.SAMPLING_INTERVAL_KEY, interval), + Map.of(SnapshotProfilingEnvVarsConfigurationFactory.SAMPLING_INTERVAL_KEY, interval), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then var duration = - properties.getDuration(SnapshotProfilingEnvVarsConfiguration.SAMPLING_INTERVAL_KEY); + properties.getDuration(SnapshotProfilingEnvVarsConfigurationFactory.SAMPLING_INTERVAL_KEY); log.assertContains( - SnapshotProfilingEnvVarsConfiguration.SAMPLING_INTERVAL_KEY + " : " + duration); + SnapshotProfilingEnvVarsConfigurationFactory.SAMPLING_INTERVAL_KEY + " : " + duration); } @ParameterizedTest @@ -375,17 +380,17 @@ void includeSnapshotProfilingExportInterval(String interval) { // given var properties = DefaultConfigProperties.create( - Map.of(SnapshotProfilingEnvVarsConfiguration.EXPORT_INTERVAL_KEY, interval), + Map.of(SnapshotProfilingEnvVarsConfigurationFactory.EXPORT_INTERVAL_KEY, interval), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then var duration = - properties.getDuration(SnapshotProfilingEnvVarsConfiguration.EXPORT_INTERVAL_KEY); + properties.getDuration(SnapshotProfilingEnvVarsConfigurationFactory.EXPORT_INTERVAL_KEY); log.assertContains( - SnapshotProfilingEnvVarsConfiguration.EXPORT_INTERVAL_KEY + " : " + duration); + SnapshotProfilingEnvVarsConfigurationFactory.EXPORT_INTERVAL_KEY + " : " + duration); } @ParameterizedTest @@ -395,14 +400,14 @@ void includeSnapshotProfilingStagingCapacity(int capacity) { var properties = DefaultConfigProperties.create( Map.of( - SnapshotProfilingEnvVarsConfiguration.STAGING_CAPACITY_KEY, + SnapshotProfilingEnvVarsConfigurationFactory.STAGING_CAPACITY_KEY, String.valueOf(capacity)), COMPONENT_LOADER); // when - new SnapshotProfilingEnvVarsConfiguration(properties).log(); + SnapshotProfilingEnvVarsConfigurationFactory.create(properties).log(); // then log.assertContains( - SnapshotProfilingEnvVarsConfiguration.STAGING_CAPACITY_KEY + " : " + capacity); + SnapshotProfilingEnvVarsConfigurationFactory.STAGING_CAPACITY_KEY + " : " + capacity); } }*/ } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java index 339a468eb..f5d3d153b 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivatorTest.java @@ -16,106 +16,102 @@ package com.splunk.opentelemetry.profiler.snapshot; -import static com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.createAutoConfiguredSdk; import static org.junit.jupiter.api.Assertions.assertInstanceOf; 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 io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; +import com.splunk.opentelemetry.profiler.OtelLoggerFactory; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.OpenTelemetrySdkExtension; -import java.io.IOException; -import java.nio.file.Path; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; +import io.opentelemetry.sdk.common.export.HttpSenderProvider; +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; class StackTraceExporterActivatorTest { + @RegisterExtension + public final OpenTelemetrySdkExtension sdk = OpenTelemetrySdkExtension.configure().build(); + @AfterEach void tearDown() { SpanTracker.SUPPLIER.reset(); StackTraceSampler.SUPPLIER.reset(); StagingArea.SUPPLIER.reset(); - SnapshotProfilingDeclarativeConfiguration.SUPPLIER.reset(); + SnapshotProfilingConfiguration.SUPPLIER.reset(); StackTraceExporter.SUPPLIER.reset(); } - @Nested - class SnapshotProfilingEnabled { - @RegisterExtension - public final OpenTelemetrySdkExtension s = - OpenTelemetrySdkExtension.configure() - .withProperty("splunk.snapshot.profiler.enabled", "true") - .with(new StackTraceExporterActivator()) + @Test + void configureStackTraceExporterProvider() { + var logExporter = InMemoryLogRecordExporter.create(); + SnapshotProfilingConfiguration configuration = + SnapshotProfilingConfiguration.builder() + .setEnabled(true) + .setConfigProperties(sdk.getConfig()) .build(); + SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - @Test - void configureStackTraceExporterProvider() { - var exporter = StackTraceExporter.SUPPLIER.get(); - assertNotSame(StackTraceExporter.NOOP, exporter); - assertInstanceOf(AsyncStackTraceExporter.class, exporter); - } + new StackTraceExporterActivator( + new OtelLoggerFactory(() -> logExporter, declarativeConfigProperties -> logExporter)) + .afterAgent(sdk); + + var exporter = StackTraceExporter.SUPPLIER.get(); + assertNotSame(StackTraceExporter.NOOP, exporter); + assertInstanceOf(AsyncStackTraceExporter.class, exporter); } - @Nested - class SnapshotProfilingDisabled { - @RegisterExtension - public final OpenTelemetrySdkExtension s = - OpenTelemetrySdkExtension.configure() - .withProperty("splunk.snapshot.profiler.enabled", "false") - .with(new StackTraceExporterActivator()) + @Test + void declarativeConfigWithoutExporterPreservesComponentLoader() { + RecordingComponentLoader componentLoader = new RecordingComponentLoader(); + DeclarativeConfigProperties configProperties = + YamlDeclarativeConfigProperties.create(Collections.emptyMap(), componentLoader); + SnapshotProfilingConfiguration configuration = + SnapshotProfilingConfiguration.builder() + .setEnabled(true) + .setConfigProperties(configProperties) .build(); + SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - @Test - void doNotConfigureStackTraceExporterProvider() { - var exporter = StackTraceExporter.SUPPLIER.get(); - assertSame(StackTraceExporter.NOOP, exporter); - } + new StackTraceExporterActivator().afterAgent(sdk); + + var exporter = StackTraceExporter.SUPPLIER.get(); + assertNotSame(StackTraceExporter.NOOP, exporter); + assertInstanceOf(AsyncStackTraceExporter.class, exporter); + assertTrue(componentLoader.loaded(HttpSenderProvider.class)); } - @Nested - class DeclarativeConfig { - @RegisterExtension - static final AutoCleanupExtension autoCleanup = AutoCleanupExtension.create(); - - @Test - void configureStackTraceExporterProvider(@TempDir Path tempDir) throws IOException { - String yaml = - """ - file_format: "1.0" - distribution: - splunk: - profiling: - exporter: - otlp_log_http: - callgraphs: - """; - var sdk = createAutoConfiguredSdk(yaml, tempDir, autoCleanup); - - new StackTraceExporterActivator().afterAgent(sdk); - - var exporter = StackTraceExporter.SUPPLIER.get(); - assertNotSame(StackTraceExporter.NOOP, exporter); - assertInstanceOf(AsyncStackTraceExporter.class, exporter); + @Test + void doNotConfigureStackTraceExporterProvider() { + SnapshotProfilingConfiguration configuration = + SnapshotProfilingConfiguration.builder().setEnabled(false).build(); + SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); + + new StackTraceExporterActivator().afterAgent(sdk); + + var exporter = StackTraceExporter.SUPPLIER.get(); + assertSame(StackTraceExporter.NOOP, exporter); + } + + private static class RecordingComponentLoader implements ComponentLoader { + private final ComponentLoader delegate = + ComponentLoader.forClassLoader(StackTraceExporterActivatorTest.class.getClassLoader()); + private final List> loadedSpiClasses = new ArrayList<>(); + + @Override + public Iterable load(Class spiClass) { + loadedSpiClasses.add(spiClass); + return delegate.load(spiClass); } - @Test - void doNotConfigureStackTraceExporterProviderWhenNoCallgraphs(@TempDir Path tempDir) - throws IOException { - String yaml = - """ - file_format: "1.0" - distribution: - splunk: - profiling: - """; - var sdk = createAutoConfiguredSdk(yaml, tempDir, autoCleanup); - - new StackTraceExporterActivator().afterAgent(sdk); - - var exporter = StackTraceExporter.SUPPLIER.get(); - assertSame(StackTraceExporter.NOOP, exporter); + boolean loaded(Class spiClass) { + return loadedSpiClasses.contains(spiClass); } } } From 5c93e2bc5ec0bcb82fe4ab3589b65f616d4fcc89 Mon Sep 17 00:00:00 2001 From: robsunday Date: Tue, 7 Jul 2026 17:15:20 +0200 Subject: [PATCH 02/12] Fix for missing nodes in profiler exporter config --- .../profiler/LogExporterBuilder.java | 14 ++--- .../PeriodicRecordingFlusherFactory.java | 18 +----- .../ProfilerConfigurationInitializer.java | 17 +----- .../snapshot/StackTraceExporterActivator.java | 18 +----- .../profiler/LogExporterBuilderTest.java | 60 +++++++++++++++++++ 5 files changed, 76 insertions(+), 51 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java index f45344e43..60ebedba0 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java @@ -16,10 +16,10 @@ package com.splunk.opentelemetry.profiler; -import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_LOGS; import com.google.common.annotations.VisibleForTesting; +import com.splunk.opentelemetry.profiler.util.DeclarativeConfigPropertiesUtil; import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.common.ComponentLoader; @@ -30,7 +30,6 @@ import io.opentelemetry.exporter.otlp.internal.OtlpHttpLogRecordExporterComponentProvider; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedDeclarativeConfigProperties; @@ -61,7 +60,8 @@ static LogRecordExporter fromDeclarativeConfig( if (propertyKeys.contains("otlp_log_http")) { DeclarativeConfigProperties otlpHttp = - exporterConfigProperties.getStructured("otlp_log_http", empty()); + DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( + exporterConfigProperties, "otlp_log_http"); OtlpHttpLogRecordExporterComponentProvider provider = new OtlpHttpLogRecordExporterComponentProvider(); return provider.create(toExtended(otlpHttp)); @@ -69,7 +69,8 @@ static LogRecordExporter fromDeclarativeConfig( if (propertyKeys.contains("otlp_log_grpc")) { DeclarativeConfigProperties otlpGrpc = - exporterConfigProperties.getStructured("otlp_log_grpc", empty()); + DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( + exporterConfigProperties, "otlp_log_grpc"); OtlpGrpcLogRecordExporterComponentProvider provider = new OtlpGrpcLogRecordExporterComponentProvider(); return provider.create(toExtended(otlpGrpc)); @@ -84,9 +85,8 @@ private static DeclarativeConfigProperties defaultOtlpHttpConfig( Map properties = new HashMap<>(); properties.put("endpoint", DEFAULT_HTTP_LOG_ENDPOINT); properties.put("encoding", "protobuf"); - // Keep the original component loader so OTLP sender providers remain discoverable. - return YamlDeclarativeConfigProperties.create( - properties, exporterConfigProperties.getComponentLoader()); + return DeclarativeConfigPropertiesUtil.createPreservingLoader( + exporterConfigProperties, properties); } static LogRecordExporter fromEnvironmentConfig() { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherFactory.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherFactory.java index 22f77adea..35db26b42 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherFactory.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherFactory.java @@ -23,8 +23,8 @@ import com.splunk.opentelemetry.profiler.context.SpanContextualizer; import com.splunk.opentelemetry.profiler.exporter.CpuEventExporter; import com.splunk.opentelemetry.profiler.exporter.PprofCpuEventExporter; +import com.splunk.opentelemetry.profiler.util.DeclarativeConfigPropertiesUtil; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.export.LogRecordExporter; @@ -35,7 +35,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; -import java.util.Collections; import java.util.Map; class PeriodicRecordingFlusherFactory { @@ -147,24 +146,13 @@ private StackTraceFilter buildStackTraceFilter( private static LogRecordExporter createLogRecordExporter(Object configProperties) { if (configProperties instanceof DeclarativeConfigProperties) { DeclarativeConfigProperties exporterConfig = - getExporterConfig((DeclarativeConfigProperties) configProperties); + DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( + (DeclarativeConfigProperties) configProperties, "exporter"); return LogExporterBuilder.fromDeclarativeConfig(exporterConfig); } return LogExporterBuilder.fromEnvironmentConfig(); } - private static DeclarativeConfigProperties getExporterConfig( - DeclarativeConfigProperties configProperties) { - DeclarativeConfigProperties exporterConfig = configProperties.getStructured("exporter"); - if (exporterConfig != null) { - return exporterConfig; - } - // DeclarativeConfigProperties.empty() is not used because it does not preserve the original - // component loader that is used later when creating default exporter - return YamlDeclarativeConfigProperties.create( - Collections.emptyMap(), configProperties.getComponentLoader()); - } - private boolean checkOutputDir(Path outputDir) { if (!Files.exists(outputDir)) { // Try creating the directory for the user... diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java index 518ae6564..1d5d22156 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java @@ -17,11 +17,11 @@ package com.splunk.opentelemetry.profiler; import com.google.auto.service.AutoService; +import com.splunk.opentelemetry.profiler.util.DeclarativeConfigPropertiesUtil; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import java.util.Collections; @@ -44,7 +44,8 @@ public void customize(DeclarativeConfigurationCustomizer configurationCustomizer (model) -> { DeclarativeConfigProperties distributionConfig = AutoConfigureUtil.getDistributionConfig(model); - DeclarativeConfigProperties profilingConfig = getProfilingConfig(distributionConfig); + DeclarativeConfigProperties profilingConfig = + DeclarativeConfigPropertiesUtil.getStructuredOrEmpty(distributionConfig, "profiling"); ProfilerConfiguration.SUPPLIER.configure( ProfilerDeclarativeConfigurationFactory.create(profilingConfig)); @@ -68,16 +69,4 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { public int order() { return Integer.MAX_VALUE; } - - private static DeclarativeConfigProperties getProfilingConfig( - DeclarativeConfigProperties distributionConfig) { - DeclarativeConfigProperties profilingConfig = distributionConfig.getStructured("profiling"); - if (profilingConfig != null) { - return profilingConfig; - } - // DeclarativeConfigProperties.empty() is not used because it does not preserve the original - // component loader that is used later when creating default exporter - return YamlDeclarativeConfigProperties.create( - Collections.emptyMap(), distributionConfig.getComponentLoader()); - } } 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 index 1a0bd3770..1f474d529 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java @@ -19,15 +19,14 @@ 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.declarativeconfig.YamlDeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import java.util.Collections; @AutoService(AgentListener.class) public class StackTraceExporterActivator implements AgentListener { @@ -66,7 +65,8 @@ private Logger buildLogger(AutoConfiguredOpenTelemetrySdk sdk, Object configProp Resource resource = AutoConfigureUtil.getResource(sdk); if (configProperties instanceof DeclarativeConfigProperties) { DeclarativeConfigProperties exporterConfig = - getExporterConfig((DeclarativeConfigProperties) configProperties); + DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( + (DeclarativeConfigProperties) configProperties, "exporter"); return otelLoggerFactory.build(exporterConfig, resource); } if (configProperties instanceof ConfigProperties) { @@ -75,16 +75,4 @@ private Logger buildLogger(AutoConfiguredOpenTelemetrySdk sdk, Object configProp throw new IllegalArgumentException( "Unsupported config properties type: " + configProperties.getClass().getName()); } - - private static DeclarativeConfigProperties getExporterConfig( - DeclarativeConfigProperties configProperties) { - DeclarativeConfigProperties exporterConfig = configProperties.getStructured("exporter"); - if (exporterConfig != null) { - return exporterConfig; - } - // DeclarativeConfigProperties.empty() would drop the component loader that discovers OTLP - // sender providers. - return YamlDeclarativeConfigProperties.create( - Collections.emptyMap(), configProperties.getComponentLoader()); - } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java index b95ec00cd..528162070 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java @@ -31,14 +31,21 @@ import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.common.export.GrpcSenderProvider; +import io.opentelemetry.sdk.common.export.HttpSenderProvider; import io.opentelemetry.sdk.logs.export.LogRecordExporter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -249,6 +256,36 @@ void shouldCreateGrpcExporter_defaultEndpoint() { assertThat(exporter).isNotNull(); } + @Test + void shouldCreateHttpExporterWithEmptyNodeUsingParentComponentLoader() { + // given + RecordingComponentLoader componentLoader = new RecordingComponentLoader(); + DeclarativeConfigProperties exporterConfig = + exporterConfigWithEmptyNode("otlp_log_http", componentLoader); + + // when + LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig); + + // then + assertThat(exporter).isNotNull(); + assertThat(componentLoader.loaded(HttpSenderProvider.class)).isTrue(); + } + + @Test + void shouldCreateGrpcExporterWithEmptyNodeUsingParentComponentLoader() { + // given + RecordingComponentLoader componentLoader = new RecordingComponentLoader(); + DeclarativeConfigProperties exporterConfig = + exporterConfigWithEmptyNode("otlp_log_grpc", componentLoader); + + // when + LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig); + + // then + assertThat(exporter).isNotNull(); + assertThat(componentLoader.loaded(GrpcSenderProvider.class)).isTrue(); + } + @Test void shouldThrowExceptionForInvalidProtocol() { // given @@ -274,5 +311,28 @@ private static DeclarativeConfigProperties getExporterConfig( OpenTelemetryConfigurationModel model) { return getProfilingConfig(model).getStructured("exporter", empty()); } + + private static DeclarativeConfigProperties exporterConfigWithEmptyNode( + String exporterName, ComponentLoader componentLoader) { + Map properties = new HashMap<>(); + properties.put(exporterName, null); + return YamlDeclarativeConfigProperties.create(properties, componentLoader); + } + + private static class RecordingComponentLoader implements ComponentLoader { + private final ComponentLoader delegate = + ComponentLoader.forClassLoader(LogExporterBuilderTest.class.getClassLoader()); + private final List> loadedSpiClasses = new ArrayList<>(); + + @Override + public Iterable load(Class spiClass) { + loadedSpiClasses.add(spiClass); + return delegate.load(spiClass); + } + + boolean loaded(Class spiClass) { + return loadedSpiClasses.contains(spiClass); + } + } } } From efcacf67461419d402011b569af6e5c1adfd5f8b Mon Sep 17 00:00:00 2001 From: robsunday Date: Tue, 7 Jul 2026 17:18:10 +0200 Subject: [PATCH 03/12] Fix for missing nodes in profiler exporter config --- .../util/DeclarativeConfigPropertiesUtil.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/util/DeclarativeConfigPropertiesUtil.java diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/DeclarativeConfigPropertiesUtil.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/DeclarativeConfigPropertiesUtil.java new file mode 100644 index 000000000..4b0a4dac5 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/DeclarativeConfigPropertiesUtil.java @@ -0,0 +1,45 @@ +/* + * 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.util; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.YamlDeclarativeConfigProperties; +import java.util.Collections; +import java.util.Map; + +public final class DeclarativeConfigPropertiesUtil { + private DeclarativeConfigPropertiesUtil() {} + + public static DeclarativeConfigProperties getStructuredOrEmpty( + DeclarativeConfigProperties parent, String name) { + DeclarativeConfigProperties structured = parent.getStructured(name); + if (structured != null) { + return structured; + } + return emptyPreservingLoader(parent); + } + + public static DeclarativeConfigProperties emptyPreservingLoader( + DeclarativeConfigProperties parent) { + return createPreservingLoader(parent, Collections.emptyMap()); + } + + public static DeclarativeConfigProperties createPreservingLoader( + DeclarativeConfigProperties parent, Map properties) { + return YamlDeclarativeConfigProperties.create(properties, parent.getComponentLoader()); + } +} From eff65447b21e00d855d299d058570c1bdc12fc88 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 8 Jul 2026 13:28:17 +0200 Subject: [PATCH 04/12] Code review followup --- .../profiler/ProfilerConfiguration.java | 37 +++++++++---------- .../SnapshotProfilingConfiguration.java | 19 ++++------ 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java index 70490d9e8..52fe8a398 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java @@ -74,7 +74,23 @@ public static Builder builder() { } public Builder toBuilder() { - return new Builder(this); + return new Builder() + .setEnabled(enabled) + .setIngestUrl(ingestUrl) + .setOtlpProtocol(otlpProtocol) + .setMemoryEnabled(memoryEnabled) + .setMemoryEventRateLimitEnabled(memoryEventRateLimitEnabled) + .setMemoryEventRate(memoryEventRate) + .setUseAllocationSampleEvent(useAllocationSampleEvent) + .setCallStackInterval(callStackInterval) + .setIncludeAgentInternalStacks(includeAgentInternalStacks) + .setIncludeJvmInternalStacks(includeJvmInternalStacks) + .setTracingStacksOnly(tracingStacksOnly) + .setStackDepth(stackDepth) + .setKeepFiles(keepFiles) + .setProfilerDirectory(profilerDirectory) + .setRecordingDuration(recordingDuration) + .setConfigProperties(configProperties); } public boolean isEnabled() { @@ -246,25 +262,6 @@ public static class Builder { private Builder() {} - private Builder(ProfilerConfiguration config) { - enabled = config.enabled; - ingestUrl = config.ingestUrl; - otlpProtocol = config.otlpProtocol; - memoryEnabled = config.memoryEnabled; - memoryEventRateLimitEnabled = config.memoryEventRateLimitEnabled; - memoryEventRate = config.memoryEventRate; - useAllocationSampleEvent = config.useAllocationSampleEvent; - callStackInterval = config.callStackInterval; - includeAgentInternalStacks = config.includeAgentInternalStacks; - includeJvmInternalStacks = config.includeJvmInternalStacks; - tracingStacksOnly = config.tracingStacksOnly; - stackDepth = config.stackDepth; - keepFiles = config.keepFiles; - profilerDirectory = config.profilerDirectory; - recordingDuration = config.recordingDuration; - configProperties = config.configProperties; - } - public ProfilerConfiguration build() { return new ProfilerConfiguration(this); } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java index baf0781cc..79e300f12 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingConfiguration.java @@ -59,7 +59,14 @@ public static Builder builder() { } public Builder toBuilder() { - return new Builder(this); + return new Builder() + .setEnabled(enabled) + .setSnapshotSelectionProbability(snapshotSelectionProbability) + .setStackDepth(stackDepth) + .setSamplingInterval(samplingInterval) + .setExportInterval(exportInterval) + .setStagingCapacity(stagingCapacity) + .setConfigProperties(configProperties); } public void log() { @@ -171,16 +178,6 @@ public static class Builder { private Builder() {} - private Builder(SnapshotProfilingConfiguration config) { - enabled = config.enabled; - snapshotSelectionProbability = config.snapshotSelectionProbability; - stackDepth = config.stackDepth; - samplingInterval = config.samplingInterval; - exportInterval = config.exportInterval; - stagingCapacity = config.stagingCapacity; - configProperties = config.configProperties; - } - public SnapshotProfilingConfiguration build() { return new SnapshotProfilingConfiguration(this); } From 898d25ed11397744cb7415b455da3fe508e3e7b5 Mon Sep 17 00:00:00 2001 From: robsunday Date: Tue, 14 Jul 2026 17:08:47 +0200 Subject: [PATCH 05/12] Initial commit --- .../profiler/ProfilingSupervisor.java | 5 +- .../SnapshotProfilingAgentListener.java | 52 ++++++++++ ...filingConfigurationCustomizerProvider.java | 25 ++++- .../SnapshotProfilingSdkCustomizer.java | 75 ++++++++------- .../snapshot/SnapshotProfilingSupervisor.java | 96 +++++++++++++++++++ .../snapshot/StackTraceExporterActivator.java | 78 --------------- .../StackTraceSamplerInitializer.java | 33 ++++++- .../ConcurrentServiceEntrySamplingTest.java | 2 +- .../snapshot/GracefulShutdownTest.java | 2 +- ...> SnapshotProfilingAgentListenerTest.java} | 10 +- .../SnapshotProfilingLogExportingTest.java | 2 +- 11 files changed, 250 insertions(+), 130 deletions(-) create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java rename profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/{StackTraceExporterActivatorTest.java => SnapshotProfilingAgentListenerTest.java} (93%) 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/SnapshotProfilingAgentListener.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java new file mode 100644 index 000000000..4126e1047 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java @@ -0,0 +1,52 @@ +/* + * 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 static final java.util.logging.Logger logger = + java.util.logging.Logger.getLogger(SnapshotProfilingAgentListener.class.getName()); + + private final Function snapshotProfilingSupervisorFactory; + + public SnapshotProfilingAgentListener() { + this(SnapshotProfilingSupervisor::initialize); + } + + @VisibleForTesting + SnapshotProfilingAgentListener(Function snapshotProfilingSupervisorFactory) { + this.snapshotProfilingSupervisorFactory = snapshotProfilingSupervisorFactory; + } + + @Override + public void afterAgent(AutoConfiguredOpenTelemetrySdk sdk) { + SnapshotProfilingSupervisor supervisor = snapshotProfilingSupervisorFactory.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..bcf8bd534 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,18 +49,33 @@ OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel m SnapshotProfilingConfiguration snapshotProfiling = getSnapshotProfilingConfig(model); SnapshotProfilingConfiguration.SUPPLIER.configure(snapshotProfiling); + // Rzeczy konfigurowany dla snapshot profilera +// +// W StackTraceSamplerInitializer: +// ⁃ StagingArea.SUPPLIER +// ⁃ StackTraceSampler.SUPPLIER +// +// W ContextStorageWrapper +// ⁃ SpanTracker.SUPPLIER +// To cos trzeba będzie więcej pomachać +// +// W SnapshotProfilingConfigurationCustomizerProvider są dodawane dwa providery +// ⁃ SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor +// ⁃ SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) + + if (snapshotProfiling.isEnabled()) { initActiveSpansTracking(); - initStackTraceSampler(snapshotProfiling); +// initStackTraceSampler(snapshotProfiling); addSpanProcessors(model); } return model; } - - private void initStackTraceSampler(SnapshotProfilingConfiguration snapshotProfilingConfig) { - StackTraceSamplerInitializer.setupStackTraceSampler(snapshotProfilingConfig); - } +// +// private void initStackTraceSampler(SnapshotProfilingConfiguration snapshotProfilingConfig) { +// StackTraceSamplerInitializer.setupStackTraceSampler(snapshotProfilingConfig); +// } private void addSpanProcessors(OpenTelemetryConfigurationModel model) { TracerProviderModel tracerProviderModel = model.getTracerProvider(); 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..32631f86a 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 @@ -32,46 +32,47 @@ @AutoService(AutoConfigurationCustomizerProvider.class) public class SnapshotProfilingSdkCustomizer implements AutoConfigurationCustomizerProvider { private final TraceRegistry registry; - private final Function samplerProvider; +// private final Function samplerProvider; private final ContextStorageWrapper contextStorageWrapper; public SnapshotProfilingSdkCustomizer() { this( TraceRegistryHolder.getTraceRegistry(), - stackTraceSamplerProvider(), +// 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); - } +// 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); +// } +// +// @VisibleForTesting +// SnapshotProfilingSdkCustomizer( +// TraceRegistry registry, +//// StackTraceSampler sampler, +// ContextStorageWrapper contextStorageWrapper) { +// this(registry, properties -> sampler, contextStorageWrapper); +// } @VisibleForTesting SnapshotProfilingSdkCustomizer( TraceRegistry registry, - StackTraceSampler sampler, - ContextStorageWrapper contextStorageWrapper) { - this(registry, properties -> sampler, contextStorageWrapper); - } - - private SnapshotProfilingSdkCustomizer( - TraceRegistry registry, - Function samplerProvider, +// Function samplerProvider, ContextStorageWrapper contextStorageWrapper) { this.registry = registry; - this.samplerProvider = samplerProvider; +// this.samplerProvider = samplerProvider; this.contextStorageWrapper = contextStorageWrapper; } @@ -82,7 +83,7 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { autoConfigurationCustomizer .addTracerProviderCustomizer(snapshotProfilingSpanProcessor(registry)) - .addPropertiesCustomizer(setupStackTraceSampler()) +// .addPropertiesCustomizer(setupStackTraceSampler()) .addPropertiesCustomizer(startTrackingActiveSpans(registry)) .addTracerProviderCustomizer(addShutdownHook()); } @@ -125,16 +126,16 @@ private boolean includeTraceContextPropagator(Set configuredPropagators) return configuredPropagators.isEmpty(); } - private Function> setupStackTraceSampler() { - return properties -> { - if (snapshotProfilingEnabled()) { - StackTraceSampler sampler = samplerProvider.apply(properties); - ConfigurableSupplier supplier = StackTraceSampler.SUPPLIER; - supplier.configure(sampler); - } - return Collections.emptyMap(); - }; - } +// private Function> setupStackTraceSampler() { +// return properties -> { +// if (snapshotProfilingEnabled()) { +// StackTraceSampler sampler = samplerProvider.apply(properties); +// ConfigurableSupplier supplier = StackTraceSampler.SUPPLIER; +// supplier.configure(sampler); +// } +// return Collections.emptyMap(); +// }; +// } private Function> startTrackingActiveSpans( TraceRegistry registry) { 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..47df2b979 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java @@ -0,0 +1,96 @@ +package com.splunk.opentelemetry.profiler.snapshot; + +import com.google.common.annotations.VisibleForTesting; +import com.splunk.opentelemetry.profiler.OtelLoggerFactory; +import com.splunk.opentelemetry.profiler.ProfilingSupervisor; +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 AutoConfiguredOpenTelemetrySdk sdk; + private final OtelLoggerFactory otelLoggerFactory; + private boolean running; + + @VisibleForTesting + SnapshotProfilingSupervisor( + OptionalConfigurableSupplier configurationSupplier, + AutoConfiguredOpenTelemetrySdk sdk, + OtelLoggerFactory otelLoggerFactory + ) { + this.configurationSupplier = configurationSupplier; + 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, + sdk, + new OtelLoggerFactory() + ); + SUPPLIER.configure(supervisor); + + return supervisor; + } + + public synchronized void startProfiling() { + if (running) { + return; + } + + // TODO: Is it needed + if (!configurationSupplier.get().isEnabled()) { + throw new IllegalStateException("Snapshot profiling not enabled in configuration"); + } + + configurationSupplier.get().log(); + + // StagingArea.SUPPLIER + // StackTraceSampler.SUPPLIER + // StackTraceExporter.SUPPLIER = AsyncStackTraceExporter + StackTraceSamplerInitializer.setupStackTraceSampler(configurationSupplier.get()); + StackTraceSamplerInitializer.setupStackTraceExporter( + configurationSupplier.get(), + AutoConfigureUtil.getResource(sdk), + otelLoggerFactory + ); + + // SpanTracker.SUPPLIER + // SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor + // SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) + + 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(); + + running = false; + } + +} 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..da1fc6aa1 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,18 @@ 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.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +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 +39,32 @@ 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/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java index 70f6a8934..56fbeaf70 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 @@ -64,7 +64,7 @@ private StackTraceSampler newSampler(StagingArea staging) { .withProperty("splunk.snapshot.selection.probability", "1.0") .with(downstreamCustomizer) .with( - new StackTraceExporterActivator( + new SnapshotProfilingAgentListener( new OtelLoggerFactory( () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); 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..b58debf1c 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 @@ -41,7 +41,7 @@ class GracefulShutdownTest { .withProperty("splunk.snapshot.selection.probability", "1.0") .with(Snapshotting.customizer().withRealStackTraceSampler().withRealStagingArea().build()) .with( - new StackTraceExporterActivator( + new SnapshotProfilingAgentListener( new OtelLoggerFactory( () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); 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 93% 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..4bd6d9205 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 @@ -35,7 +35,7 @@ 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(); @@ -58,7 +58,7 @@ void configureStackTraceExporterProvider() { .build(); SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - new StackTraceExporterActivator( + new SnapshotProfilingAgentListener( new OtelLoggerFactory(() -> logExporter, declarativeConfigProperties -> logExporter)) .afterAgent(sdk); @@ -79,7 +79,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 +93,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 +101,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/SnapshotProfilingLogExportingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java index db71b2917..b6f5db8e7 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( + new SnapshotProfilingAgentListener( new OtelLoggerFactory( () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); From 73b5c6d509d7cc763f733ed97f6e088d0953cf9a Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 15 Jul 2026 08:50:01 +0200 Subject: [PATCH 06/12] Test fixes and cleanup of comments --- .../SnapshotProfilingAgentListener.java | 23 ++++++--- ...filingConfigurationCustomizerProvider.java | 20 -------- .../SnapshotProfilingSdkCustomizer.java | 41 ---------------- .../snapshot/SnapshotProfilingSupervisor.java | 47 +++++++++++-------- .../StackTraceSamplerInitializer.java | 15 +++--- .../ConcurrentServiceEntrySamplingTest.java | 7 --- .../SnapshotProfilingAgentListenerTest.java | 9 ++++ ...SnapshotProfilingSdkCustomizerBuilder.java | 2 +- 8 files changed, 61 insertions(+), 103 deletions(-) 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 index 4126e1047..71f52986f 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java @@ -18,29 +18,38 @@ import com.google.auto.service.AutoService; import com.google.common.annotations.VisibleForTesting; +import com.splunk.opentelemetry.profiler.OtelLoggerFactory; 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 static final java.util.logging.Logger logger = - java.util.logging.Logger.getLogger(SnapshotProfilingAgentListener.class.getName()); - - private final Function snapshotProfilingSupervisorFactory; + private final Function + snapshotProfilingSupervisorMaker; public SnapshotProfilingAgentListener() { this(SnapshotProfilingSupervisor::initialize); } @VisibleForTesting - SnapshotProfilingAgentListener(Function snapshotProfilingSupervisorFactory) { - this.snapshotProfilingSupervisorFactory = snapshotProfilingSupervisorFactory; + SnapshotProfilingAgentListener(OtelLoggerFactory otelLoggerFactory) { + this( + sdk -> + new SnapshotProfilingSupervisor( + SnapshotProfilingConfiguration.SUPPLIER, sdk, otelLoggerFactory)); + } + + private SnapshotProfilingAgentListener( + Function + snapshotProfilingSupervisorMaker) { + this.snapshotProfilingSupervisorMaker = snapshotProfilingSupervisorMaker; } @Override public void afterAgent(AutoConfiguredOpenTelemetrySdk sdk) { - SnapshotProfilingSupervisor supervisor = snapshotProfilingSupervisorFactory.apply(sdk); + // Must be always executed to initialize supervisor + SnapshotProfilingSupervisor supervisor = snapshotProfilingSupervisorMaker.apply(sdk); SnapshotProfilingConfiguration configuration = SnapshotProfilingConfiguration.SUPPLIER.get(); if (!configuration.isEnabled()) { 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 bcf8bd534..9061cf528 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,33 +49,13 @@ OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel m SnapshotProfilingConfiguration snapshotProfiling = getSnapshotProfilingConfig(model); SnapshotProfilingConfiguration.SUPPLIER.configure(snapshotProfiling); - // Rzeczy konfigurowany dla snapshot profilera -// -// W StackTraceSamplerInitializer: -// ⁃ StagingArea.SUPPLIER -// ⁃ StackTraceSampler.SUPPLIER -// -// W ContextStorageWrapper -// ⁃ SpanTracker.SUPPLIER -// To cos trzeba będzie więcej pomachać -// -// W SnapshotProfilingConfigurationCustomizerProvider są dodawane dwa providery -// ⁃ SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor -// ⁃ SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) - - if (snapshotProfiling.isEnabled()) { initActiveSpansTracking(); -// initStackTraceSampler(snapshotProfiling); addSpanProcessors(model); } return model; } -// -// private void initStackTraceSampler(SnapshotProfilingConfiguration snapshotProfilingConfig) { -// StackTraceSamplerInitializer.setupStackTraceSampler(snapshotProfilingConfig); -// } private void addSpanProcessors(OpenTelemetryConfigurationModel model) { TracerProviderModel tracerProviderModel = model.getTracerProvider(); 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 32631f86a..a74ad2dc9 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,7 +22,6 @@ 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; @@ -32,47 +31,19 @@ @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); -// } -// -// @VisibleForTesting -// SnapshotProfilingSdkCustomizer( -// TraceRegistry registry, -//// StackTraceSampler sampler, -// ContextStorageWrapper contextStorageWrapper) { -// this(registry, properties -> sampler, contextStorageWrapper); -// } - @VisibleForTesting SnapshotProfilingSdkCustomizer( TraceRegistry registry, -// Function samplerProvider, ContextStorageWrapper contextStorageWrapper) { this.registry = registry; -// this.samplerProvider = samplerProvider; this.contextStorageWrapper = contextStorageWrapper; } @@ -83,7 +54,6 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { autoConfigurationCustomizer .addTracerProviderCustomizer(snapshotProfilingSpanProcessor(registry)) -// .addPropertiesCustomizer(setupStackTraceSampler()) .addPropertiesCustomizer(startTrackingActiveSpans(registry)) .addTracerProviderCustomizer(addShutdownHook()); } @@ -126,17 +96,6 @@ private boolean includeTraceContextPropagator(Set configuredPropagators) return configuredPropagators.isEmpty(); } -// private Function> setupStackTraceSampler() { -// return properties -> { -// if (snapshotProfilingEnabled()) { -// StackTraceSampler sampler = samplerProvider.apply(properties); -// ConfigurableSupplier supplier = StackTraceSampler.SUPPLIER; -// supplier.configure(sampler); -// } -// return Collections.emptyMap(); -// }; -// } - private Function> startTrackingActiveSpans( TraceRegistry registry) { return properties -> { 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 index 47df2b979..fe16ab969 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java @@ -1,8 +1,23 @@ +/* + * 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.ProfilingSupervisor; import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; @@ -11,7 +26,8 @@ public class SnapshotProfilingSupervisor { public static final OptionalConfigurableSupplier SUPPLIER = new OptionalConfigurableSupplier<>(); - private static final Logger logger = Logger.getLogger(SnapshotProfilingSupervisor.class.getName()); + private static final Logger logger = + Logger.getLogger(SnapshotProfilingSupervisor.class.getName()); private final OptionalConfigurableSupplier configurationSupplier; private final AutoConfiguredOpenTelemetrySdk sdk; @@ -22,25 +38,20 @@ public class SnapshotProfilingSupervisor { SnapshotProfilingSupervisor( OptionalConfigurableSupplier configurationSupplier, AutoConfiguredOpenTelemetrySdk sdk, - OtelLoggerFactory otelLoggerFactory - ) { + OtelLoggerFactory otelLoggerFactory) { this.configurationSupplier = configurationSupplier; 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, - sdk, - new OtelLoggerFactory() - ); + SnapshotProfilingSupervisor supervisor = + new SnapshotProfilingSupervisor( + SnapshotProfilingConfiguration.SUPPLIER, sdk, new OtelLoggerFactory()); SUPPLIER.configure(supervisor); return supervisor; @@ -58,19 +69,16 @@ public synchronized void startProfiling() { configurationSupplier.get().log(); - // StagingArea.SUPPLIER - // StackTraceSampler.SUPPLIER + // StagingArea.SUPPLIER + // StackTraceSampler.SUPPLIER // StackTraceExporter.SUPPLIER = AsyncStackTraceExporter StackTraceSamplerInitializer.setupStackTraceSampler(configurationSupplier.get()); StackTraceSamplerInitializer.setupStackTraceExporter( - configurationSupplier.get(), - AutoConfigureUtil.getResource(sdk), - otelLoggerFactory - ); + configurationSupplier.get(), AutoConfigureUtil.getResource(sdk), otelLoggerFactory); // SpanTracker.SUPPLIER - // SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor - // SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) + // SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor + // SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) running = true; logger.info("Snapshot profiling is active."); @@ -92,5 +100,4 @@ public synchronized void stopProfiling() { running = false; } - } 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 da1fc6aa1..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 @@ -20,8 +20,6 @@ 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.AutoConfigureUtil; -import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; import java.time.Duration; @@ -39,9 +37,13 @@ static void setupStackTraceSampler(SnapshotProfilingConfiguration configuration) StackTraceSampler.SUPPLIER.configure(sampler); } - static void setupStackTraceExporter(SnapshotProfilingConfiguration configuration, Resource resource, OtelLoggerFactory otelLoggerFactory) { + static void setupStackTraceExporter( + SnapshotProfilingConfiguration configuration, + Resource resource, + OtelLoggerFactory otelLoggerFactory) { int maxDepth = configuration.getStackDepth(); - Logger otelLogger = buildLogger(otelLoggerFactory, resource, configuration.getConfigProperties()); + Logger otelLogger = + buildLogger(otelLoggerFactory, resource, configuration.getConfigProperties()); AsyncStackTraceExporter exporter = new AsyncStackTraceExporter(otelLogger, maxDepth); StackTraceExporter.SUPPLIER.configure(exporter); } @@ -52,8 +54,8 @@ private static StagingArea createStagingArea(SnapshotProfilingConfiguration conf return new PeriodicallyExportingStagingArea(StackTraceExporter.SUPPLIER, interval, capacity); } - - private static Logger buildLogger(OtelLoggerFactory otelLoggerFactory, Resource resource , Object configProperties) { + private static Logger buildLogger( + OtelLoggerFactory otelLoggerFactory, Resource resource, Object configProperties) { if (configProperties instanceof DeclarativeConfigProperties) { DeclarativeConfigProperties exporterConfig = DeclarativeConfigPropertiesUtil.getStructuredOrEmpty( @@ -66,5 +68,4 @@ private static Logger buildLogger(OtelLoggerFactory otelLoggerFactory, Resource throw new IllegalArgumentException( "Unsupported config properties type: " + configProperties.getClass().getName()); } - } 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 56fbeaf70..fca8b7326 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,7 +27,6 @@ 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; @@ -43,7 +41,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); @@ -63,10 +60,6 @@ private StackTraceSampler newSampler(StagingArea staging) { .withProperty("splunk.snapshot.profiler.enabled", "true") .withProperty("splunk.snapshot.selection.probability", "1.0") .with(downstreamCustomizer) - .with( - new SnapshotProfilingAgentListener( - new OtelLoggerFactory( - () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); @RegisterExtension diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java index 4bd6d9205..6c260aee9 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java @@ -32,6 +32,7 @@ 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; @@ -41,6 +42,14 @@ class SnapshotProfilingAgentListenerTest { @AfterEach void tearDown() { + resetSnapshotProfiling(); + } + + private void resetSnapshotProfiling() { + if (SnapshotProfilingSupervisor.SUPPLIER.isConfigured()) { + SnapshotProfilingSupervisor.SUPPLIER.get().stopProfiling(); + SnapshotProfilingSupervisor.SUPPLIER.reset(); + } SpanTracker.SUPPLIER.reset(); StackTraceSampler.SUPPLIER.reset(); StagingArea.SUPPLIER.reset(); 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..4258415c7 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 @@ -67,6 +67,6 @@ SnapshotProfilingSdkCustomizerBuilder with(ContextStorageWrapper contextStorageW } SnapshotProfilingSdkCustomizer build() { - return new SnapshotProfilingSdkCustomizer(registry, sampler, contextStorageWrapper); + return new SnapshotProfilingSdkCustomizer(registry, contextStorageWrapper); } } From 576149f140d0216fa8af211b7b76ab1701651ba4 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 15 Jul 2026 09:17:51 +0200 Subject: [PATCH 07/12] Tests cleanup --- .../snapshot/ConcurrentServiceEntrySamplingTest.java | 2 +- .../snapshot/SnapshotProfilingSdkCustomizerBuilder.java | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) 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 fca8b7326..f0db3d0c1 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 @@ -52,7 +52,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 = 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 4258415c7..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; } From 6f66ce4522dc362e944dfbd9af27fc1690f98532 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 15 Jul 2026 15:59:06 +0200 Subject: [PATCH 08/12] SpanTracker support --- .../profiler/snapshot/ActiveSpanTracker.java | 16 ++++++++++++++ .../SnapshotProfilingAgentListener.java | 1 + ...filingConfigurationCustomizerProvider.java | 2 +- .../SnapshotProfilingSdkCustomizer.java | 20 ++++-------------- .../snapshot/SnapshotProfilingSupervisor.java | 3 +++ .../profiler/snapshot/SpanTracker.java | 21 ++++++++++++++++++- .../snapshot/ActiveSpanTrackerTest.java | 19 +++++++++++++++++ .../ConcurrentServiceEntrySamplingTest.java | 6 ++++++ .../snapshot/ContextStorageWrapperTest.java | 2 ++ .../snapshot/InMemorySpanTracker.java | 11 ++++++++++ .../SnapshotProfilingAgentListenerTest.java | 1 - 11 files changed, 83 insertions(+), 19 deletions(-) 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/SnapshotProfilingAgentListener.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java index 71f52986f..a39ac8cf3 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java @@ -32,6 +32,7 @@ public SnapshotProfilingAgentListener() { this(SnapshotProfilingSupervisor::initialize); } + // TODO: Get rid of this constructor @VisibleForTesting SnapshotProfilingAgentListener(OtelLoggerFactory otelLoggerFactory) { this( 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 9061cf528..5f756febf 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,8 +49,8 @@ OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel m SnapshotProfilingConfiguration snapshotProfiling = getSnapshotProfilingConfig(model); SnapshotProfilingConfiguration.SUPPLIER.configure(snapshotProfiling); + initActiveSpansTracking(); if (snapshotProfiling.isEnabled()) { - initActiveSpansTracking(); addSpanProcessors(model); } 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 a74ad2dc9..20a60fd19 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 @@ -24,7 +24,6 @@ import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; @@ -34,15 +33,12 @@ public class SnapshotProfilingSdkCustomizer implements AutoConfigurationCustomiz private final ContextStorageWrapper contextStorageWrapper; public SnapshotProfilingSdkCustomizer() { - this( - TraceRegistryHolder.getTraceRegistry(), - new ContextStorageWrapper()); + this(TraceRegistryHolder.getTraceRegistry(), new ContextStorageWrapper()); } @VisibleForTesting SnapshotProfilingSdkCustomizer( - TraceRegistry registry, - ContextStorageWrapper contextStorageWrapper) { + TraceRegistry registry, ContextStorageWrapper contextStorageWrapper) { this.registry = registry; this.contextStorageWrapper = contextStorageWrapper; } @@ -70,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; }; } @@ -92,16 +86,10 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { }; } - private boolean includeTraceContextPropagator(Set configuredPropagators) { - return configuredPropagators.isEmpty(); - } - private Function> startTrackingActiveSpans( TraceRegistry registry) { return properties -> { - if (snapshotProfilingEnabled()) { - contextStorageWrapper.wrapContextStorage(registry); - } + contextStorageWrapper.wrapContextStorage(registry); return Collections.emptyMap(); }; } 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 index fe16ab969..ff3698da0 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java @@ -77,6 +77,7 @@ public synchronized void startProfiling() { configurationSupplier.get(), AutoConfigureUtil.getResource(sdk), otelLoggerFactory); // SpanTracker.SUPPLIER + SpanTracker.SUPPLIER.get().setEnabled(true); // SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor // SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) @@ -98,6 +99,8 @@ public synchronized void stopProfiling() { StackTraceExporter.SUPPLIER.get().close(); StackTraceExporter.SUPPLIER.reset(); + SpanTracker.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/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 f0db3d0c1..22d0583b4 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 @@ -33,6 +33,7 @@ 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; @@ -88,6 +89,11 @@ private StackTraceSampler newSampler(StagingArea staging) { .performing(concurrentExitCallsTo(2, downstream, upstreamSdk)) .build(); + @BeforeEach + void enableActiveSpanTracking() { + SpanTracker.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..b45219a87 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); @@ -88,6 +89,7 @@ void spanTrackingRunsBeforeThreadChangeDetector() { StackTraceSampler.SUPPLIER.configure(sampler); wrapper.wrapContextStorage(registry); + SpanTracker.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/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/SnapshotProfilingAgentListenerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java index 6c260aee9..946fd6e6d 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java @@ -32,7 +32,6 @@ 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; From 656daea65199b541abe2fd7e560c5d7f31601dd6 Mon Sep 17 00:00:00 2001 From: robsunday Date: Fri, 17 Jul 2026 11:52:02 +0200 Subject: [PATCH 09/12] TraceThreadChangeDetector support --- .../snapshot/ContextStorageWrapper.java | 4 ++- .../SnapshotProfilingSdkCustomizer.java | 13 ++++------ .../snapshot/SnapshotProfilingSupervisor.java | 12 ++++----- .../snapshot/TraceThreadChangeDetector.java | 17 +++++++++++++ .../TraceThreadChangeDetectorTest.java | 25 +++++++++++++++++++ 5 files changed, 55 insertions(+), 16 deletions(-) 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..74c7f3821 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,8 @@ 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/SnapshotProfilingSdkCustomizer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java index 20a60fd19..199917bcb 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 @@ -74,15 +74,12 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { private BiFunction snapshotProfilingSpanProcessor(TraceRegistry registry) { return (builder, properties) -> { - if (snapshotProfilingEnabled()) { - double selectionProbability = - SnapshotProfilingConfiguration.SUPPLIER.get().getSnapshotSelectionProbability(); + double selectionProbability = + SnapshotProfilingConfiguration.SUPPLIER.get().getSnapshotSelectionProbability(); - return builder.addSpanProcessor( - new SnapshotProfilingSpanProcessor( - registry, new TraceIdBasedSnapshotSelector(selectionProbability))); - } - return builder; + return builder.addSpanProcessor( + new SnapshotProfilingSpanProcessor( + registry, new TraceIdBasedSnapshotSelector(selectionProbability))); }; } 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 index ff3698da0..07d539e94 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java @@ -62,11 +62,6 @@ public synchronized void startProfiling() { return; } - // TODO: Is it needed - if (!configurationSupplier.get().isEnabled()) { - throw new IllegalStateException("Snapshot profiling not enabled in configuration"); - } - configurationSupplier.get().log(); // StagingArea.SUPPLIER @@ -78,8 +73,10 @@ public synchronized void startProfiling() { // SpanTracker.SUPPLIER SpanTracker.SUPPLIER.get().setEnabled(true); - // SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor - // SdkShutdownHookComponentProvider -> SdkShutdownHook (raczej nie wymaga zmian) + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); + + // SnapshotProfilingSdkCustomizer/SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor + // SdkShutdownHookComponentProvider -> SdkShutdownHook running = true; logger.info("Snapshot profiling is active."); @@ -100,6 +97,7 @@ public synchronized void stopProfiling() { 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/TraceThreadChangeDetector.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/TraceThreadChangeDetector.java index 5ab79f00f..b0c5659b1 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,14 @@ 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 +41,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/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()); } From 900e1f8339d325174e119789fb0249f58d092335 Mon Sep 17 00:00:00 2001 From: robsunday Date: Fri, 17 Jul 2026 12:45:22 +0200 Subject: [PATCH 10/12] Tests updated after TraceThreadChangeDetector support --- .../snapshot/ContextStorageWrapper.java | 3 ++- .../SnapshotProfilingAgentListener.java | 6 +++++- .../snapshot/SnapshotProfilingSupervisor.java | 20 +++++++++++++++---- .../snapshot/TraceThreadChangeDetector.java | 3 ++- .../ConcurrentServiceEntrySamplingTest.java | 3 ++- .../snapshot/ContextStorageWrapperTest.java | 2 ++ .../LongRunningBackgroundTaskTest.java | 6 ++++++ .../MultiThreadedTraceProfilingTest.java | 6 ++++++ .../SnapshotProfilingAgentListenerTest.java | 8 ++++++++ ...ngConfigurationCustomizerProviderTest.java | 1 + .../profiler/snapshot/TraceProfilingTest.java | 6 ++++++ 11 files changed, 56 insertions(+), 8 deletions(-) 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 74c7f3821..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,7 +49,8 @@ private ContextStorage trackActiveSpans(ContextStorage storage, TraceRegistry re } private ContextStorage detectThreadChanges(ContextStorage storage, TraceRegistry registry) { - TraceThreadChangeDetector detector = 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 index a39ac8cf3..840e99de5 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java @@ -38,7 +38,11 @@ public SnapshotProfilingAgentListener() { this( sdk -> new SnapshotProfilingSupervisor( - SnapshotProfilingConfiguration.SUPPLIER, sdk, otelLoggerFactory)); + SnapshotProfilingConfiguration.SUPPLIER, + SpanTracker.SUPPLIER, + TraceThreadChangeDetector.SUPPLIER, + sdk, + otelLoggerFactory)); } private SnapshotProfilingAgentListener( 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 index 07d539e94..0a0161c7a 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSupervisor.java @@ -30,6 +30,9 @@ public class SnapshotProfilingSupervisor { 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; @@ -37,9 +40,13 @@ public class SnapshotProfilingSupervisor { @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; } @@ -51,7 +58,11 @@ public static SnapshotProfilingSupervisor initialize(AutoConfiguredOpenTelemetry SnapshotProfilingSupervisor supervisor = new SnapshotProfilingSupervisor( - SnapshotProfilingConfiguration.SUPPLIER, sdk, new OtelLoggerFactory()); + SnapshotProfilingConfiguration.SUPPLIER, + SpanTracker.SUPPLIER, + TraceThreadChangeDetector.SUPPLIER, + sdk, + new OtelLoggerFactory()); SUPPLIER.configure(supervisor); return supervisor; @@ -72,10 +83,11 @@ public synchronized void startProfiling() { configurationSupplier.get(), AutoConfigureUtil.getResource(sdk), otelLoggerFactory); // SpanTracker.SUPPLIER - SpanTracker.SUPPLIER.get().setEnabled(true); - TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); + spanTrackerSupplier.get().setEnabled(true); + traceThreadChangeDetectorSupplier.get().setEnabled(true); - // SnapshotProfilingSdkCustomizer/SnapshotProfilingSpanProcessorComponentProvider -> SnapshotProfilingSpanProcessor + // SnapshotProfilingSdkCustomizer/SnapshotProfilingSpanProcessorComponentProvider -> + // SnapshotProfilingSpanProcessor // SdkShutdownHookComponentProvider -> SdkShutdownHook running = true; 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 b0c5659b1..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 @@ -26,7 +26,8 @@ import javax.annotation.Nullable; class TraceThreadChangeDetector implements ContextStorage { - static OptionalConfigurableSupplier SUPPLIER = new OptionalConfigurableSupplier<>(); + static OptionalConfigurableSupplier SUPPLIER = + new OptionalConfigurableSupplier<>(); private final ContextStorage delegate; private final TraceRegistry registry; 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 22d0583b4..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 @@ -90,8 +90,9 @@ private StackTraceSampler newSampler(StagingArea staging) { .build(); @BeforeEach - void enableActiveSpanTracking() { + void enableContextTracking() { SpanTracker.SUPPLIER.get().setEnabled(true); + TraceThreadChangeDetector.SUPPLIER.get().setEnabled(true); } /** 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 b45219a87..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 @@ -62,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(); } @@ -90,6 +91,7 @@ void spanTrackingRunsBeforeThreadChangeDetector() { 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/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/SnapshotProfilingAgentListenerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java index 946fd6e6d..a74acaa32 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.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,6 +33,7 @@ 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; @@ -39,6 +41,11 @@ class SnapshotProfilingAgentListenerTest { @RegisterExtension public final OpenTelemetrySdkExtension sdk = OpenTelemetrySdkExtension.configure().build(); + @BeforeEach + void setUp() { + TraceThreadChangeDetector.SUPPLIER.configure(mock(TraceThreadChangeDetector.class)); + } + @AfterEach void tearDown() { resetSnapshotProfiling(); @@ -50,6 +57,7 @@ private void resetSnapshotProfiling() { SnapshotProfilingSupervisor.SUPPLIER.reset(); } SpanTracker.SUPPLIER.reset(); + TraceThreadChangeDetector.SUPPLIER.reset(); StackTraceSampler.SUPPLIER.reset(); StagingArea.SUPPLIER.reset(); SnapshotProfilingConfiguration.SUPPLIER.reset(); 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..d90b90d3a 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 @@ -44,6 +44,7 @@ void resetSuppliers() { StackTraceSampler.SUPPLIER.reset(); StagingArea.SUPPLIER.reset(); SpanTracker.SUPPLIER.reset(); + TraceThreadChangeDetector.SUPPLIER.reset(); } @Test 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()) { From a7aa8c6b5df6980c792fa82b15a48f78ef2e50c2 Mon Sep 17 00:00:00 2001 From: robsunday Date: Fri, 17 Jul 2026 13:02:41 +0200 Subject: [PATCH 11/12] Tests improved --- .../SnapshotProfilingAgentListener.java | 15 +---------- .../snapshot/GracefulShutdownTest.java | 9 ++++++- .../SnapshotProfilingAgentListenerTest.java | 17 ++----------- .../SnapshotProfilingLogExportingTest.java | 2 +- .../profiler/snapshot/Snapshotting.java | 25 +++++++++++++++++++ 5 files changed, 37 insertions(+), 31 deletions(-) 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 index 840e99de5..cf0653b9a 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListener.java @@ -18,7 +18,6 @@ import com.google.auto.service.AutoService; import com.google.common.annotations.VisibleForTesting; -import com.splunk.opentelemetry.profiler.OtelLoggerFactory; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import java.util.function.Function; @@ -32,20 +31,8 @@ public SnapshotProfilingAgentListener() { this(SnapshotProfilingSupervisor::initialize); } - // TODO: Get rid of this constructor @VisibleForTesting - SnapshotProfilingAgentListener(OtelLoggerFactory otelLoggerFactory) { - this( - sdk -> - new SnapshotProfilingSupervisor( - SnapshotProfilingConfiguration.SUPPLIER, - SpanTracker.SUPPLIER, - TraceThreadChangeDetector.SUPPLIER, - sdk, - otelLoggerFactory)); - } - - private SnapshotProfilingAgentListener( + SnapshotProfilingAgentListener( Function snapshotProfilingSupervisorMaker) { this.snapshotProfilingSupervisorMaker = snapshotProfilingSupervisorMaker; 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 b58debf1c..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 SnapshotProfilingAgentListener( + 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/SnapshotProfilingAgentListenerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java index a74acaa32..65ac03a7e 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingAgentListenerTest.java @@ -48,20 +48,7 @@ void setUp() { @AfterEach void tearDown() { - resetSnapshotProfiling(); - } - - private void resetSnapshotProfiling() { - 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(); + Snapshotting.resetProfiling(); } @Test @@ -74,7 +61,7 @@ void configureStackTraceExporterProvider() { .build(); SnapshotProfilingConfiguration.SUPPLIER.configure(configuration); - new SnapshotProfilingAgentListener( + Snapshotting.agentListener( new OtelLoggerFactory(() -> logExporter, declarativeConfigProperties -> logExporter)) .afterAgent(sdk); 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 b6f5db8e7..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 SnapshotProfilingAgentListener( + Snapshotting.agentListener( new OtelLoggerFactory( () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); 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() From 0538bb5c090ca9250c6cc874e3a005b1aa3159eb Mon Sep 17 00:00:00 2001 From: robsunday Date: Fri, 17 Jul 2026 14:44:25 +0200 Subject: [PATCH 12/12] Declarative config customizer improvements --- ...ProfilingConfigurationCustomizerProvider.java | 4 +--- .../snapshot/SnapshotProfilingSdkCustomizer.java | 4 ---- ...ilingConfigurationCustomizerProviderTest.java | 16 +++++++++++----- 3 files changed, 12 insertions(+), 12 deletions(-) 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 5f756febf..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 @@ -50,9 +50,7 @@ OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel m SnapshotProfilingConfiguration.SUPPLIER.configure(snapshotProfiling); initActiveSpansTracking(); - if (snapshotProfiling.isEnabled()) { - addSpanProcessors(model); - } + addSpanProcessors(model); return model; } 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 199917bcb..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 @@ -90,8 +90,4 @@ private Function> startTrackingActiveSpans return Collections.emptyMap(); }; } - - private boolean snapshotProfilingEnabled() { - return SnapshotProfilingConfiguration.SUPPLIER.get().isEnabled(); - } } 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 d90b90d3a..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(); @@ -48,7 +45,7 @@ void resetSuppliers() { } @Test - void shouldDoNothingIfProfilerIsNotEnabled(@TempDir Path tempDir) throws IOException { + void shouldAddComponentsNeededToEnableProfilingIfProfilerIsNotEnabledInitially() { // given String yaml = """ @@ -59,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