Skip to content

Commit bd562fd

Browse files
committed
Add configuration option for controlling the snapshot profiling sampling rate.
1 parent a5bfcbc commit bd562fd

File tree

8 files changed

+88
-33
lines changed

8 files changed

+88
-33
lines changed

profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ public class Configuration implements AutoConfigurationCustomizerProvider {
7777
private static final String CONFIG_KEY_SNAPSHOT_PROFILER_STACK_DEPTH =
7878
"splunk.snapshot.profiler.max.stack.depth";
7979
private static final int DEFAULT_SNAPSHOT_PROFILER_STACK_DEPTH = 1024;
80+
private static final String CONFIG_KEY_SNAPSHOT_PROFILER_SAMPLING_INTERVAL =
81+
"splunk.snapshot.profiler.sampling.interval";
82+
public static final Duration DEFAULT_SNAPSHOT_PROFILER_SAMPLING_INTERVAL = Duration.ofMillis(20);
8083

8184
@Override
8285
public void customize(AutoConfigurationCustomizer autoConfiguration) {
@@ -219,4 +222,12 @@ public static int getSnapshotProfilerStackDepth(ConfigProperties properties) {
219222
return properties.getInt(
220223
CONFIG_KEY_SNAPSHOT_PROFILER_STACK_DEPTH, DEFAULT_SNAPSHOT_PROFILER_STACK_DEPTH);
221224
}
225+
226+
public static Duration getSnapshotProfilerSamplingInterval(ConfigProperties properties) {
227+
int millis = properties.getInt(CONFIG_KEY_SNAPSHOT_PROFILER_SAMPLING_INTERVAL, 0);
228+
if (millis > 0) {
229+
return Duration.ofMillis(millis);
230+
}
231+
return DEFAULT_SNAPSHOT_PROFILER_SAMPLING_INTERVAL;
232+
}
222233
}

profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/AsyncStackTraceExporter.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616

1717
package com.splunk.opentelemetry.profiler.snapshot;
1818

19-
import com.google.common.annotations.VisibleForTesting;
2019
import com.splunk.opentelemetry.profiler.InstrumentationSource;
2120
import com.splunk.opentelemetry.profiler.exporter.CpuEventExporter;
2221
import com.splunk.opentelemetry.profiler.exporter.PprofCpuEventExporter;
2322
import io.opentelemetry.api.logs.Logger;
24-
import java.time.Clock;
25-
import java.time.Instant;
23+
import java.time.Duration;
2624
import java.util.List;
2725
import java.util.concurrent.ExecutorService;
2826
import java.util.concurrent.Executors;
@@ -34,18 +32,13 @@ class AsyncStackTraceExporter implements StackTraceExporter {
3432

3533
private final ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
3634
private final Logger otelLogger;
35+
private final Duration samplingPeriod;
3736
private final int maxDepth;
38-
private final Clock clock;
3937

40-
AsyncStackTraceExporter(Logger logger, int maxDepth) {
41-
this(logger, maxDepth, Clock.systemUTC());
42-
}
43-
44-
@VisibleForTesting
45-
AsyncStackTraceExporter(Logger logger, int maxDepth, Clock clock) {
38+
AsyncStackTraceExporter(Logger logger, Duration samplingPeriod, int maxDepth) {
4639
this.otelLogger = logger;
40+
this.samplingPeriod = samplingPeriod;
4741
this.maxDepth = maxDepth;
48-
this.clock = clock;
4942
}
5043

5144
@Override
@@ -60,18 +53,17 @@ private Runnable pprofExporter(Logger otelLogger, List<StackTrace> stackTraces)
6053
PprofCpuEventExporter.builder()
6154
.otelLogger(otelLogger)
6255
.stackDepth(maxDepth)
63-
.period(ScheduledExecutorStackTraceSampler.SCHEDULER_PERIOD)
56+
.period(samplingPeriod)
6457
.instrumentationSource(InstrumentationSource.SNAPSHOT)
6558
.build();
6659

67-
Instant now = Instant.now(clock);
6860
for (StackTrace stackTrace : stackTraces) {
6961
cpuEventExporter.export(
7062
stackTrace.getThreadId(),
7163
stackTrace.getThreadName(),
7264
stackTrace.getThreadState(),
7365
stackTrace.getStackFrames(),
74-
now,
66+
stackTrace.getTimestamp(),
7567
stackTrace.getTraceId(),
7668
null);
7769
}

profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/ScheduledExecutorStackTraceSampler.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package com.splunk.opentelemetry.profiler.snapshot;
1818

19-
import com.google.common.annotations.VisibleForTesting;
2019
import io.opentelemetry.api.trace.SpanContext;
2120
import java.lang.management.ManagementFactory;
2221
import java.lang.management.ThreadInfo;
@@ -36,19 +35,13 @@ class ScheduledExecutorStackTraceSampler implements StackTraceSampler {
3635
private static final Logger logger =
3736
Logger.getLogger(ScheduledExecutorStackTraceSampler.class.getName());
3837
private static final int SCHEDULER_INITIAL_DELAY = 0;
39-
static final Duration SCHEDULER_PERIOD = Duration.ofMillis(20);
4038

4139
private final ConcurrentMap<String, ScheduledExecutorService> samplers =
4240
new ConcurrentHashMap<>();
4341
private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
4442
private final StagingArea stagingArea;
4543
private final Duration samplingPeriod;
4644

47-
ScheduledExecutorStackTraceSampler(StagingArea stagingArea) {
48-
this(stagingArea, SCHEDULER_PERIOD);
49-
}
50-
51-
@VisibleForTesting
5245
ScheduledExecutorStackTraceSampler(StagingArea stagingArea, Duration samplingPeriod) {
5346
this.stagingArea = stagingArea;
5447
this.samplingPeriod = samplingPeriod;

profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizer.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
2424
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2525
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
26+
import java.time.Duration;
2627
import java.util.Collections;
2728
import java.util.LinkedHashSet;
2829
import java.util.Map;
@@ -33,19 +34,29 @@
3334
@AutoService(AutoConfigurationCustomizerProvider.class)
3435
public class SnapshotProfilingSdkCustomizer implements AutoConfigurationCustomizerProvider {
3536
private final TraceRegistry registry;
36-
private final StackTraceSampler sampler;
37+
private final Function<ConfigProperties, StackTraceSampler> samplerProvider;
3738

3839
public SnapshotProfilingSdkCustomizer() {
39-
this(
40-
new TraceRegistry(),
41-
new ScheduledExecutorStackTraceSampler(
42-
new AccumulatingStagingArea(StackTraceExporterProvider.INSTANCE)));
40+
this(new TraceRegistry(), stackTraceSamplerProvider());
41+
}
42+
43+
private static Function<ConfigProperties, StackTraceSampler> stackTraceSamplerProvider() {
44+
return properties -> {
45+
Duration samplingPeriod = Configuration.getSnapshotProfilerSamplingInterval(properties);
46+
return new ScheduledExecutorStackTraceSampler(
47+
new AccumulatingStagingArea(StackTraceExporterProvider.INSTANCE), samplingPeriod);
48+
};
4349
}
4450

4551
@VisibleForTesting
46-
SnapshotProfilingSdkCustomizer(TraceRegistry registry, StackTraceSampler sampler) {
52+
SnapshotProfilingSdkCustomizer(TraceRegistry registry, StackTraceSampler samplerProvider) {
53+
this(registry, properties -> samplerProvider);
54+
}
55+
56+
private SnapshotProfilingSdkCustomizer(
57+
TraceRegistry registry, Function<ConfigProperties, StackTraceSampler> samplerProvider) {
4758
this.registry = registry;
48-
this.sampler = sampler;
59+
this.samplerProvider = samplerProvider;
4960
}
5061

5162
@Override
@@ -59,6 +70,7 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) {
5970
snapshotProfilingSpanProcessor(TraceRegistry registry) {
6071
return (builder, properties) -> {
6172
if (snapshotProfilingEnabled(properties)) {
73+
StackTraceSampler sampler = samplerProvider.apply(properties);
6274
return builder.addSpanProcessor(new SnapshotProfilingSpanProcessor(registry, sampler));
6375
}
6476
return builder;

profiler/src/main/java/com/splunk/opentelemetry/profiler/snapshot/StackTraceExporterActivator.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
2727
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2828
import io.opentelemetry.sdk.resources.Resource;
29+
import java.time.Duration;
2930

3031
@AutoService(AgentListener.class)
3132
public class StackTraceExporterActivator implements AgentListener {
@@ -45,14 +46,20 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr
4546
ConfigProperties properties = AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
4647
if (snapshotProfilingEnabled(properties)) {
4748
int maxDepth = Configuration.getSnapshotProfilerStackDepth(properties);
48-
Resource resource = AutoConfigureUtil.getResource(autoConfiguredOpenTelemetrySdk);
49-
Logger logger = otelLoggerFactory.build(properties, resource);
50-
AsyncStackTraceExporter exporter = new AsyncStackTraceExporter(logger, maxDepth);
49+
Duration samplingPeriod = Configuration.getSnapshotProfilerSamplingInterval(properties);
50+
Logger logger = buildLogger(autoConfiguredOpenTelemetrySdk, properties);
51+
AsyncStackTraceExporter exporter =
52+
new AsyncStackTraceExporter(logger, samplingPeriod, maxDepth);
5153
StackTraceExporterProvider.INSTANCE.configure(exporter);
5254
}
5355
}
5456

5557
private boolean snapshotProfilingEnabled(ConfigProperties properties) {
5658
return Configuration.isSnapshotProfilingEnabled(properties);
5759
}
60+
61+
private Logger buildLogger(AutoConfiguredOpenTelemetrySdk sdk, ConfigProperties properties) {
62+
Resource resource = AutoConfigureUtil.getResource(sdk);
63+
return otelLoggerFactory.build(properties, resource);
64+
}
5865
}

profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
2626
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
27+
import java.time.Duration;
2728
import java.util.Collections;
2829
import java.util.HashMap;
2930
import java.util.Map;
@@ -185,4 +186,22 @@ void getDefaultSnapshotProfilerStackDepthWhenNotSpecified() {
185186
var properties = DefaultConfigProperties.create(Collections.emptyMap());
186187
assertEquals(1024, Configuration.getSnapshotProfilerStackDepth(properties));
187188
}
189+
190+
@ParameterizedTest
191+
@ValueSource(ints = {128, 512, 2056})
192+
void getConfiguredSnapshotProfilerSamplingInterval(int milliseconds) {
193+
var properties =
194+
DefaultConfigProperties.create(
195+
Map.of("splunk.snapshot.profiler.sampling.interval", String.valueOf(milliseconds)));
196+
assertEquals(
197+
Duration.ofMillis(milliseconds),
198+
Configuration.getSnapshotProfilerSamplingInterval(properties));
199+
}
200+
201+
@Test
202+
void getDefaultSnapshotProfilerSamplingInterval() {
203+
var properties = DefaultConfigProperties.create(Collections.emptyMap());
204+
assertEquals(
205+
Duration.ofMillis(20), Configuration.getSnapshotProfilerSamplingInterval(properties));
206+
}
188207
}

profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/AsyncStackTraceExporterTest.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static com.splunk.opentelemetry.profiler.ProfilingSemanticAttributes.DATA_TYPE;
2121
import static com.splunk.opentelemetry.profiler.ProfilingSemanticAttributes.FRAME_COUNT;
2222
import static com.splunk.opentelemetry.profiler.ProfilingSemanticAttributes.INSTRUMENTATION_SOURCE;
23+
import static com.splunk.opentelemetry.profiler.ProfilingSemanticAttributes.SOURCE_EVENT_TIME;
2324
import static com.splunk.opentelemetry.profiler.ProfilingSemanticAttributes.SOURCE_TYPE;
2425
import static org.assertj.core.api.Assertions.assertThat;
2526
import static org.awaitility.Awaitility.await;
@@ -30,13 +31,15 @@
3031
import com.splunk.opentelemetry.profiler.exporter.InMemoryOtelLogger;
3132
import com.splunk.opentelemetry.profiler.pprof.PprofUtils;
3233
import java.io.ByteArrayInputStream;
34+
import java.time.Duration;
3335
import java.util.List;
3436
import java.util.zip.GZIPInputStream;
3537
import org.junit.jupiter.api.Test;
3638

3739
class AsyncStackTraceExporterTest {
3840
private final InMemoryOtelLogger logger = new InMemoryOtelLogger();
39-
private final AsyncStackTraceExporter exporter = new AsyncStackTraceExporter(logger, 200);
41+
private final AsyncStackTraceExporter exporter =
42+
new AsyncStackTraceExporter(logger, Duration.ofMillis(20), 200);
4043

4144
@Test
4245
void exportStackTraceAsOpenTelemetryLog() {
@@ -153,4 +156,19 @@ void includeFrameCountOpenTelemetryAttributeInLogMessage() {
153156
assertThat(attributes.asMap())
154157
.containsEntry(FRAME_COUNT, (long) stackTrace.getStackFrames().length);
155158
}
159+
160+
@Test
161+
void includeStackTraceDurationInSamples() throws Exception {
162+
var stackTrace = Snapshotting.stackTrace().build();
163+
164+
exporter.export(List.of(stackTrace));
165+
await().until(() -> !logger.records().isEmpty());
166+
167+
var profile = Profile.parseFrom(PprofUtils.deserialize(logger.records().get(0)));
168+
var sample = profile.getSample(0);
169+
170+
var labels = PprofUtils.toLabelString(sample, profile);
171+
assertThat(labels)
172+
.containsEntry(SOURCE_EVENT_TIME.getKey(), stackTrace.getTimestamp().toEpochMilli());
173+
}
156174
}

profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingSdkCustomizerBuilder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.splunk.opentelemetry.profiler.snapshot;
1818

19+
import java.time.Duration;
20+
1921
class SnapshotProfilingSdkCustomizerBuilder {
2022
private TraceRegistry registry = new TraceRegistry();
2123
private StackTraceSampler sampler = new ObservableStackTraceSampler();
@@ -28,7 +30,8 @@ SnapshotProfilingSdkCustomizerBuilder with(TraceRegistry registry) {
2830
SnapshotProfilingSdkCustomizerBuilder withRealStackTraceSampler() {
2931
return with(
3032
new ScheduledExecutorStackTraceSampler(
31-
new AccumulatingStagingArea(StackTraceExporterProvider.INSTANCE)));
33+
new AccumulatingStagingArea(StackTraceExporterProvider.INSTANCE),
34+
Duration.ofMillis(20)));
3235
}
3336

3437
SnapshotProfilingSdkCustomizerBuilder with(StackTraceSampler sampler) {

0 commit comments

Comments
 (0)