Skip to content

Commit

Permalink
Add Snapshot Profiling Feature Flag and Scaffolding (#2170)
Browse files Browse the repository at this point in the history
* Add feature flag to enable/disable the snapshot profiler.

* Include snapshot profiling feature flag configuration in the profiling config logging output.

* Add OpenTelemetry SDK customizer scaffolding for the snapshot profiler.

* Add test for to confirm that the snapshot profiler is disabled by default.

* Add explanation comments in OpenTelemetrySdkExtension.

* Applying spotless code formatting.

* Remove unnecessary ActivationNotifier interface in favor of built in Runnable.

* Rename logger in SnapshotProfilingSdkCustomizer to lowercase.

* Remove snapshot profiler configuration logging.

* Remove unnecessary implementation of the OpenTelemetry interface in OpenTelemetrySdkExtension as that functionality is not needed by the current tests.

* Apply spotless formatting changes.

* Remove configuration logging snapshot profiling tests.

* Update license file.

* Apply spotless code formatting.
  • Loading branch information
tduncan authored Feb 3, 2025
1 parent 6cb4df4 commit d0caed9
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 1 deletion.
2 changes: 1 addition & 1 deletion licenses/licenses.md
Original file line number Diff line number Diff line change
@@ -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`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -83,6 +86,8 @@ Map<String, String> 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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<Function<ConfigProperties, Map<String, String>>> propertyCustomizers =
new ArrayList<>();

@Override
public AutoConfigurationCustomizer addTracerProviderCustomizer(
BiFunction<SdkTracerProviderBuilder, ConfigProperties, SdkTracerProviderBuilder>
tracerProviderCustomizer) {
return this;
}

@Override
public AutoConfigurationCustomizer addPropagatorCustomizer(
BiFunction<? super TextMapPropagator, ConfigProperties, ? extends TextMapPropagator>
textMapPropagator) {
return this;
}

@Override
public AutoConfigurationCustomizer addPropertiesCustomizer(
Function<ConfigProperties, Map<String, String>> propertiesCustomizer) {
this.propertyCustomizers.add(propertiesCustomizer);
return this;
}

@Override
public AutoConfigurationCustomizer addResourceCustomizer(
BiFunction<? super Resource, ConfigProperties, ? extends Resource> biFunction) {
return this;
}

@Override
public AutoConfigurationCustomizer addSamplerCustomizer(
BiFunction<? super Sampler, ConfigProperties, ? extends Sampler> biFunction) {
return null;
}

@Override
public AutoConfigurationCustomizer addSpanExporterCustomizer(
BiFunction<? super SpanExporter, ConfigProperties, ? extends SpanExporter> biFunction) {
return this;
}

@Override
public AutoConfigurationCustomizer addPropertiesSupplier(
Supplier<Map<String, String>> supplier) {
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}

0 comments on commit d0caed9

Please sign in to comment.