diff --git a/licenses/licenses.md b/licenses/licenses.md index fdb579d49..84d7b4873 100644 --- a/licenses/licenses.md +++ b/licenses/licenses.md @@ -1,7 +1,7 @@ # splunk-otel-javaagent ## Dependency License Report -_2025-01-31 10:12:43 EET_ +_2025-02-03 11:01:31 PST_ ## Apache License, Version 2.0 **1** **Group:** `com.squareup.okhttp3` **Name:** `okhttp` **Version:** `4.12.0` diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index fa4cbeca1..1501b78a3 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -69,6 +69,9 @@ public class Configuration implements AutoConfigurationCustomizerProvider { public static final String CONFIG_KEY_TRACING_STACKS_ONLY = "splunk.profiler.tracing.stacks.only"; private static final String CONFIG_KEY_STACK_DEPTH = "splunk.profiler.max.stack.depth"; + public static final String CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER = + "splunk.snapshot.profiler.enabled"; + @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { autoConfiguration.addPropertiesSupplier(this::defaultProperties); @@ -83,6 +86,8 @@ Map defaultProperties() { config.put(CONFIG_KEY_MEMORY_ENABLED, String.valueOf(DEFAULT_MEMORY_ENABLED)); config.put(CONFIG_KEY_MEMORY_EVENT_RATE, DEFAULT_MEMORY_EVENT_RATE); config.put(CONFIG_KEY_CALL_STACK_INTERVAL, DEFAULT_CALL_STACK_INTERVAL.toMillis() + "ms"); + + config.put(CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, "false"); return config; } 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 new file mode 100644 index 000000000..fd9ba05b1 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java @@ -0,0 +1,59 @@ +/* + * 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 com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER; +import static java.util.Collections.emptyMap; + +import com.google.auto.service.AutoService; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.logging.Logger; + +@AutoService(AutoConfigurationCustomizerProvider.class) +public class SnapshotProfilingSdkCustomizer implements AutoConfigurationCustomizerProvider { + private static final Logger logger = + Logger.getLogger(SnapshotProfilingSdkCustomizer.class.getName()); + + private final Runnable activationNotifier; + + public SnapshotProfilingSdkCustomizer() { + this(() -> logger.info("Snapshot profiling activated")); + } + + @VisibleForTesting + SnapshotProfilingSdkCustomizer(Runnable activationNotifier) { + this.activationNotifier = activationNotifier; + } + + @Override + public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { + autoConfigurationCustomizer.addPropertiesCustomizer( + config -> { + if (snapshotProfilingEnabled(config)) { + activationNotifier.run(); + } + return emptyMap(); + }); + } + + private boolean snapshotProfilingEnabled(ConfigProperties config) { + return config.getBoolean(CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER, false); + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java index a9381dea9..58856190f 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java @@ -96,4 +96,11 @@ void getOtlpProtocol() { String result = Configuration.getOtlpProtocol(DefaultConfigProperties.create(map)); assertEquals("test2", result); } + + @Test + void snapshotProfilingDisabledByDefault() { + Configuration configuration = new Configuration(); + var properties = configuration.defaultProperties(); + assertEquals("false", properties.get("splunk.snapshot.profiler.enabled")); + } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/OpenTelemetrySdkExtension.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/OpenTelemetrySdkExtension.java new file mode 100644 index 000000000..807da5b02 --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/OpenTelemetrySdkExtension.java @@ -0,0 +1,146 @@ +/* + * 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.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.OpenTelemetrySdkBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class OpenTelemetrySdkExtension implements AfterEachCallback { + public static Builder builder() { + return new Builder(); + } + + private final OpenTelemetrySdk sdk; + + private OpenTelemetrySdkExtension(OpenTelemetrySdk sdk) { + this.sdk = sdk; + } + + @Override + public void afterEach(ExtensionContext extensionContext) { + sdk.close(); + } + + /** + * An extremely simplified adaptation of the OpenTelemetry class + * AutoConfiguredOpenTelemetrySdkBuilder, designed explicitly to facilitate easier component-like + * testing of custom OpenTelemetry Java Agent extensions. + */ + public static class Builder { + private final SdkCustomizer customizer = new SdkCustomizer(); + private final Map properties = new HashMap<>(); + + public Builder withProperty(String name, String value) { + properties.put(name, value); + return this; + } + + public Builder with(AutoConfigurationCustomizerProvider provider) { + provider.customize(customizer); + return this; + } + + /** + * Simplified re-implementation of AutoConfiguredOpenTelemetrySdkBuilder's build method. The + * OpenTelemetry SDK is only configured with features necessary to pass existing test use cases. + */ + public OpenTelemetrySdkExtension build() { + overrideProperties(); + + OpenTelemetrySdkBuilder sdkBuilder = OpenTelemetrySdk.builder(); + OpenTelemetrySdk sdk = sdkBuilder.build(); + + return new OpenTelemetrySdkExtension(sdk); + } + + private void overrideProperties() { + var properties = DefaultConfigProperties.createFromMap(this.properties); + for (var customizer : customizer.propertyCustomizers) { + var overrides = customizer.apply(properties); + properties = properties.withOverrides(overrides); + } + } + } + + private static class SdkCustomizer implements AutoConfigurationCustomizer { + private final List>> propertyCustomizers = + new ArrayList<>(); + + @Override + public AutoConfigurationCustomizer addTracerProviderCustomizer( + BiFunction + tracerProviderCustomizer) { + return this; + } + + @Override + public AutoConfigurationCustomizer addPropagatorCustomizer( + BiFunction + textMapPropagator) { + return this; + } + + @Override + public AutoConfigurationCustomizer addPropertiesCustomizer( + Function> propertiesCustomizer) { + this.propertyCustomizers.add(propertiesCustomizer); + return this; + } + + @Override + public AutoConfigurationCustomizer addResourceCustomizer( + BiFunction biFunction) { + return this; + } + + @Override + public AutoConfigurationCustomizer addSamplerCustomizer( + BiFunction biFunction) { + return null; + } + + @Override + public AutoConfigurationCustomizer addSpanExporterCustomizer( + BiFunction biFunction) { + return this; + } + + @Override + public AutoConfigurationCustomizer addPropertiesSupplier( + Supplier> supplier) { + return this; + } + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerTest.java new file mode 100644 index 000000000..3e33b60c9 --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerTest.java @@ -0,0 +1,82 @@ +/* + * 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.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SnapshotProfilingSdkCustomizerTest { + private final ObservableActivationNotifier activationNotifier = + new ObservableActivationNotifier(); + private final SnapshotProfilingSdkCustomizer customizer = + new SnapshotProfilingSdkCustomizer(activationNotifier); + + @Nested + class TestSnapshotProfilingDisabledByDefault { + @RegisterExtension + public final OpenTelemetrySdkExtension s = + OpenTelemetrySdkExtension.builder().with(customizer).build(); + + @Test + void customizeOpenTelemetrySdk() { + assertFalse(activationNotifier.activated); + } + } + + @Nested + class TestEnableSnapshotProfiling { + @RegisterExtension + public final OpenTelemetrySdkExtension s = + OpenTelemetrySdkExtension.builder() + .with(customizer) + .withProperty("splunk.snapshot.profiler.enabled", "true") + .build(); + + @Test + void customizeOpenTelemetrySdk() { + assertTrue(activationNotifier.activated); + } + } + + @Nested + class TestDisableSnapshotProfiling { + @RegisterExtension + public final OpenTelemetrySdkExtension s = + OpenTelemetrySdkExtension.builder() + .with(customizer) + .withProperty("splunk.snapshot.profiler.enabled", "false") + .build(); + + @Test + void customizeOpenTelemetrySdk() { + assertFalse(activationNotifier.activated); + } + } + + private static class ObservableActivationNotifier implements Runnable { + private boolean activated = false; + + @Override + public void run() { + this.activated = true; + } + } +}