diff --git a/inferred-spans/README.md b/inferred-spans/README.md
index 667f0d201..f7486ad75 100644
--- a/inferred-spans/README.md
+++ b/inferred-spans/README.md
@@ -43,6 +43,31 @@ So if you are using an autoconfigured OpenTelemetry SDK, you'll only need to add
| otel.inferred.spans.lib.directory
OTEL_INFERRED_SPANS_LIB_DIRECTORY | Defaults to the value of `java.io.tmpdir` | Profiling requires that the [async-profiler](https://github.com/async-profiler/async-profiler) shared library is exported to a temporary location and loaded by the JVM. The partition backing this location must be executable, however in some server-hardened environments, `noexec` may be set on the standard `/tmp` partition, leading to `java.lang.UnsatisfiedLinkError` errors. Set this property to an alternative directory (e.g. `/var/tmp`) to resolve this. |
| otel.inferred.spans.parent.override.handler
OTEL_INFERRED_SPANS_PARENT_OVERRIDE_HANDLER | Defaults to a handler adding span-links to the inferred span | Inferred spans sometimes need to be inserted as the new parent of a normal span, which is not directly possible because that span has already been sent. For this reason, this relationship needs to be represented differently, which normally is done by adding a span-link to the inferred span. This configuration can be used to override that behaviour by providing the fully qualified name of a class implementing `BiConsumer`: The biconsumer will be invoked with the inferred span as first argument and the span for which the inferred one was detected as new parent as second argument |
+### Usage with declarative configuration
+
+You can configure the inferred spans processor using declarative YAML configuration with the
+OpenTelemetry SDK. For example:
+
+```yaml
+file_format: 1.0-rc.1
+tracer_provider:
+ processors:
+ - inferred_spans/development:
+ enabled: true # true by default unlike autoconfiguration described above
+ sampling_interval: 25ms
+ included_classes: "org.example.myapp.*"
+ excluded_classes: "java.*"
+ min_duration: 10ms
+ interval: 5s
+ duration: 5s
+ lib_directory: "/var/tmp"
+ parent_override_handler: "com.example.MyParentOverrideHandler"
+```
+
+All the same settings as for [autoconfiguration](#autoconfiguration) can be used here,
+just with the `otel.inferred.spans.` prefix stripped.
+For example, `otel.inferred.spans.sampling.interval` becomes `sampling_interval` in YAML.
+
### Manual SDK setup
If you manually set-up your `OpenTelemetrySDK`, you need to create and register an `InferredSpansProcessor` with your `TracerProvider`:
diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts
index 98d5e33a3..01522095c 100644
--- a/inferred-spans/build.gradle.kts
+++ b/inferred-spans/build.gradle.kts
@@ -12,7 +12,9 @@ dependencies {
annotationProcessor("com.google.auto.service:auto-service")
compileOnly("com.google.auto.service:auto-service-annotations")
compileOnly("io.opentelemetry:opentelemetry-sdk")
- compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
+ compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
+ compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
+ compileOnly("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge")
compileOnly("io.opentelemetry.semconv:opentelemetry-semconv")
implementation("com.lmax:disruptor")
implementation("org.jctools:jctools-core")
@@ -25,9 +27,11 @@ dependencies {
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv")
testImplementation("io.opentelemetry:opentelemetry-sdk")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
testImplementation("io.opentelemetry:opentelemetry-api-incubator")
testImplementation("io.opentelemetry:opentelemetry-exporter-logging")
+ testImplementation("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge")
}
tasks {
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfig.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfig.java
index 7ee78943e..4b8a6170e 100644
--- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfig.java
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfig.java
@@ -5,70 +5,24 @@
package io.opentelemetry.contrib.inferredspans;
-import static java.util.stream.Collectors.toList;
+import static io.opentelemetry.contrib.inferredspans.InferredSpansConfig.ENABLED_OPTION;
import com.google.auto.service.AutoService;
-import io.opentelemetry.api.trace.SpanBuilder;
-import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
-import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
import java.util.logging.Logger;
-import javax.annotation.Nullable;
@AutoService(AutoConfigurationCustomizerProvider.class)
public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvider {
private static final Logger log = Logger.getLogger(InferredSpansAutoConfig.class.getName());
- static final String ENABLED_OPTION = "otel.inferred.spans.enabled";
- static final String LOGGING_OPTION = "otel.inferred.spans.logging.enabled";
- static final String DIAGNOSTIC_FILES_OPTION = "otel.inferred.spans.backup.diagnostic.files";
- static final String SAFEMODE_OPTION = "otel.inferred.spans.safe.mode";
- static final String POSTPROCESSING_OPTION = "otel.inferred.spans.post.processing.enabled";
- static final String SAMPLING_INTERVAL_OPTION = "otel.inferred.spans.sampling.interval";
- static final String MIN_DURATION_OPTION = "otel.inferred.spans.min.duration";
- static final String INCLUDED_CLASSES_OPTION = "otel.inferred.spans.included.classes";
- static final String EXCLUDED_CLASSES_OPTION = "otel.inferred.spans.excluded.classes";
- static final String INTERVAL_OPTION = "otel.inferred.spans.interval";
- static final String DURATION_OPTION = "otel.inferred.spans.duration";
- static final String LIB_DIRECTORY_OPTION = "otel.inferred.spans.lib.directory";
- static final String PARENT_OVERRIDE_HANDLER_OPTION =
- "otel.inferred.spans.parent.override.handler";
-
@Override
public void customize(AutoConfigurationCustomizer config) {
config.addTracerProviderCustomizer(
(providerBuilder, properties) -> {
if (properties.getBoolean(ENABLED_OPTION, false)) {
- InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder();
-
- PropertiesApplier applier = new PropertiesApplier(properties);
-
- applier.applyBool(LOGGING_OPTION, builder::profilerLoggingEnabled);
- applier.applyBool(DIAGNOSTIC_FILES_OPTION, builder::backupDiagnosticFiles);
- applier.applyInt(SAFEMODE_OPTION, builder::asyncProfilerSafeMode);
- applier.applyBool(POSTPROCESSING_OPTION, builder::postProcessingEnabled);
- applier.applyDuration(SAMPLING_INTERVAL_OPTION, builder::samplingInterval);
- applier.applyDuration(MIN_DURATION_OPTION, builder::inferredSpansMinDuration);
- applier.applyWildcards(INCLUDED_CLASSES_OPTION, builder::includedClasses);
- applier.applyWildcards(EXCLUDED_CLASSES_OPTION, builder::excludedClasses);
- applier.applyDuration(INTERVAL_OPTION, builder::profilerInterval);
- applier.applyDuration(DURATION_OPTION, builder::profilingDuration);
- applier.applyString(LIB_DIRECTORY_OPTION, builder::profilerLibDirectory);
-
- String parentOverrideHandlerName = properties.getString(PARENT_OVERRIDE_HANDLER_OPTION);
- if (parentOverrideHandlerName != null && !parentOverrideHandlerName.isEmpty()) {
- builder.parentOverrideHandler(
- constructParentOverrideHandler(parentOverrideHandlerName));
- }
-
- providerBuilder.addSpanProcessor(builder.build());
+ providerBuilder.addSpanProcessor(InferredSpansConfig.createSpanProcessor(properties));
} else {
log.finest(
"Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set");
@@ -76,59 +30,4 @@ public void customize(AutoConfigurationCustomizer config) {
return providerBuilder;
});
}
-
- @SuppressWarnings("unchecked")
- private static BiConsumer constructParentOverrideHandler(String name) {
- try {
- Class> clazz = Class.forName(name);
- return (BiConsumer) clazz.getConstructor().newInstance();
- } catch (Exception e) {
- throw new IllegalArgumentException("Could not construct parent override handler", e);
- }
- }
-
- private static class PropertiesApplier {
-
- private final ConfigProperties properties;
-
- PropertiesApplier(ConfigProperties properties) {
- this.properties = properties;
- }
-
- void applyBool(String configKey, Consumer funcToApply) {
- applyValue(properties.getBoolean(configKey), funcToApply);
- }
-
- void applyInt(String configKey, Consumer funcToApply) {
- applyValue(properties.getInt(configKey), funcToApply);
- }
-
- void applyDuration(String configKey, Consumer funcToApply) {
- applyValue(properties.getDuration(configKey), funcToApply);
- }
-
- void applyString(String configKey, Consumer funcToApply) {
- applyValue(properties.getString(configKey), funcToApply);
- }
-
- void applyWildcards(String configKey, Consumer super List> funcToApply) {
- String wildcardListString = properties.getString(configKey);
- if (wildcardListString != null && !wildcardListString.isEmpty()) {
- List values =
- Arrays.stream(wildcardListString.split(","))
- .filter(str -> !str.isEmpty())
- .map(WildcardMatcher::valueOf)
- .collect(toList());
- if (!values.isEmpty()) {
- funcToApply.accept(values);
- }
- }
- }
-
- private static void applyValue(@Nullable T value, Consumer funcToApply) {
- if (value != null) {
- funcToApply.accept(value);
- }
- }
- }
}
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java
new file mode 100644
index 000000000..9b8ff2ca7
--- /dev/null
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.contrib.inferredspans;
+
+import static java.util.Collections.unmodifiableList;
+import static java.util.stream.Collectors.toList;
+
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.trace.SpanProcessor;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import javax.annotation.Nullable;
+
+class InferredSpansConfig {
+
+ private InferredSpansConfig() {}
+
+ static final String ENABLED_OPTION = "otel.inferred.spans.enabled";
+ static final String LOGGING_OPTION = "otel.inferred.spans.logging.enabled";
+ static final String DIAGNOSTIC_FILES_OPTION = "otel.inferred.spans.backup.diagnostic.files";
+ static final String SAFEMODE_OPTION = "otel.inferred.spans.safe.mode";
+ static final String POSTPROCESSING_OPTION = "otel.inferred.spans.post.processing.enabled";
+ static final String SAMPLING_INTERVAL_OPTION = "otel.inferred.spans.sampling.interval";
+ static final String MIN_DURATION_OPTION = "otel.inferred.spans.min.duration";
+ static final String INCLUDED_CLASSES_OPTION = "otel.inferred.spans.included.classes";
+ static final String EXCLUDED_CLASSES_OPTION = "otel.inferred.spans.excluded.classes";
+ static final String INTERVAL_OPTION = "otel.inferred.spans.interval";
+ static final String DURATION_OPTION = "otel.inferred.spans.duration";
+ static final String LIB_DIRECTORY_OPTION = "otel.inferred.spans.lib.directory";
+ static final String PARENT_OVERRIDE_HANDLER_OPTION =
+ "otel.inferred.spans.parent.override.handler";
+
+ static final List ALL_PROPERTIES =
+ unmodifiableList(
+ Arrays.asList(
+ ENABLED_OPTION,
+ LOGGING_OPTION,
+ DIAGNOSTIC_FILES_OPTION,
+ SAFEMODE_OPTION,
+ POSTPROCESSING_OPTION,
+ SAMPLING_INTERVAL_OPTION,
+ MIN_DURATION_OPTION,
+ INCLUDED_CLASSES_OPTION,
+ EXCLUDED_CLASSES_OPTION,
+ INTERVAL_OPTION,
+ DURATION_OPTION,
+ LIB_DIRECTORY_OPTION,
+ PARENT_OVERRIDE_HANDLER_OPTION));
+
+ static SpanProcessor createSpanProcessor(ConfigProperties properties) {
+ InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder().profilerEnabled(true);
+
+ PropertiesApplier applier = new PropertiesApplier(properties);
+
+ applier.applyBool(LOGGING_OPTION, builder::profilerLoggingEnabled);
+ applier.applyBool(DIAGNOSTIC_FILES_OPTION, builder::backupDiagnosticFiles);
+ applier.applyInt(SAFEMODE_OPTION, builder::asyncProfilerSafeMode);
+ applier.applyBool(POSTPROCESSING_OPTION, builder::postProcessingEnabled);
+ applier.applyDuration(SAMPLING_INTERVAL_OPTION, builder::samplingInterval);
+ applier.applyDuration(MIN_DURATION_OPTION, builder::inferredSpansMinDuration);
+ applier.applyWildcards(INCLUDED_CLASSES_OPTION, builder::includedClasses);
+ applier.applyWildcards(EXCLUDED_CLASSES_OPTION, builder::excludedClasses);
+ applier.applyDuration(INTERVAL_OPTION, builder::profilerInterval);
+ applier.applyDuration(DURATION_OPTION, builder::profilingDuration);
+ applier.applyString(LIB_DIRECTORY_OPTION, builder::profilerLibDirectory);
+
+ String parentOverrideHandlerName = properties.getString(PARENT_OVERRIDE_HANDLER_OPTION);
+ if (parentOverrideHandlerName != null && !parentOverrideHandlerName.isEmpty()) {
+ builder.parentOverrideHandler(constructParentOverrideHandler(parentOverrideHandlerName));
+ }
+
+ return builder.build();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static BiConsumer constructParentOverrideHandler(String name) {
+ try {
+ Class> clazz = Class.forName(name);
+ return (BiConsumer) clazz.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Could not construct parent override handler", e);
+ }
+ }
+
+ private static class PropertiesApplier {
+
+ private final ConfigProperties properties;
+
+ PropertiesApplier(ConfigProperties properties) {
+ this.properties = properties;
+ }
+
+ void applyBool(String configKey, Consumer funcToApply) {
+ applyValue(properties.getBoolean(configKey), funcToApply);
+ }
+
+ void applyInt(String configKey, Consumer funcToApply) {
+ applyValue(properties.getInt(configKey), funcToApply);
+ }
+
+ void applyDuration(String configKey, Consumer funcToApply) {
+ applyValue(properties.getDuration(configKey), funcToApply);
+ }
+
+ void applyString(String configKey, Consumer funcToApply) {
+ applyValue(properties.getString(configKey), funcToApply);
+ }
+
+ void applyWildcards(String configKey, Consumer super List> funcToApply) {
+ String wildcardListString = properties.getString(configKey);
+ if (wildcardListString != null && !wildcardListString.isEmpty()) {
+ List values =
+ Arrays.stream(wildcardListString.split(","))
+ .filter(str -> !str.isEmpty())
+ .map(WildcardMatcher::valueOf)
+ .collect(toList());
+ if (!values.isEmpty()) {
+ funcToApply.accept(values);
+ }
+ }
+ }
+
+ private static void applyValue(@Nullable T value, Consumer funcToApply) {
+ if (value != null) {
+ funcToApply.accept(value);
+ }
+ }
+ }
+}
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessor.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessor.java
index c5399002e..27d1da9b7 100644
--- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessor.java
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessor.java
@@ -51,7 +51,8 @@ public class InferredSpansProcessor implements SpanProcessor {
@Nullable File activationEventsFile,
@Nullable File jfrFile) {
this.config = config;
- profiler = new SamplingProfiler(config, clock, this::getTracer, activationEventsFile, jfrFile);
+ profiler =
+ new SamplingProfiler(config, clock, this::getTracer, activationEventsFile, jfrFile, null);
if (startScheduledProfiling) {
profiler.start();
}
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessorBuilder.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessorBuilder.java
index e8f52ed68..a96f3b942 100644
--- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessorBuilder.java
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessorBuilder.java
@@ -20,6 +20,7 @@
@SuppressWarnings("CanIgnoreReturnValueSuggester")
public class InferredSpansProcessorBuilder {
+ private boolean enabled = true;
private boolean profilerLoggingEnabled = true;
private boolean backupDiagnosticFiles = false;
private int asyncProfilerSafeMode = 0;
@@ -59,6 +60,7 @@ public class InferredSpansProcessorBuilder {
public InferredSpansProcessor build() {
InferredSpansConfiguration config =
new InferredSpansConfiguration(
+ enabled,
profilerLoggingEnabled,
backupDiagnosticFiles,
asyncProfilerSafeMode,
@@ -78,6 +80,11 @@ public InferredSpansProcessor build() {
return processor;
}
+ public InferredSpansProcessorBuilder profilerEnabled(boolean profilerEnabled) {
+ this.enabled = profilerEnabled;
+ return this;
+ }
+
/**
* By default, async profiler prints warning messages about missing JVM symbols to standard
* output. Set this option to {@code false} to suppress such messages.
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java
new file mode 100644
index 000000000..19a1afd90
--- /dev/null
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.contrib.inferredspans;
+
+import com.google.auto.service.AutoService;
+import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
+import io.opentelemetry.sdk.trace.SpanProcessor;
+import java.util.logging.Logger;
+
+@SuppressWarnings("rawtypes")
+@AutoService(ComponentProvider.class)
+public class InferredSpansSpanProcessorProvider implements ComponentProvider {
+
+ private static final Logger log =
+ Logger.getLogger(InferredSpansSpanProcessorProvider.class.getName());
+
+ private static final String PREFIX = "otel.inferred.spans.";
+
+ @Override
+ public String getName() {
+ return "inferred_spans/development";
+ }
+
+ @Override
+ public SpanProcessor create(DeclarativeConfigProperties declarativeConfigProperties) {
+ DeclarativeConfigPropertiesBridgeBuilder builder =
+ new DeclarativeConfigPropertiesBridgeBuilder();
+
+ for (String property : InferredSpansConfig.ALL_PROPERTIES) {
+ // 1. crop the prefix, because the properties are under the "inferred_spans/development"
+ // 2. we want all properties flat under "otel.inferred.spans.*"
+ builder.addMapping(property, property.substring(PREFIX.length()).replace('.', '_'));
+ }
+
+ ConfigProperties properties = builder.build(declarativeConfigProperties);
+ if (properties.getBoolean(InferredSpansConfig.ENABLED_OPTION, true)) {
+ return InferredSpansConfig.createSpanProcessor(properties);
+ } else {
+ log.finest("Not enabling inferred spans processor because enabled=false");
+ return SpanProcessor.composite();
+ }
+ }
+
+ @Override
+ public Class getType() {
+ return SpanProcessor.class;
+ }
+}
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/InferredSpansConfiguration.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/InferredSpansConfiguration.java
index 4034c560d..545c36484 100644
--- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/InferredSpansConfiguration.java
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/InferredSpansConfiguration.java
@@ -15,6 +15,7 @@
public class InferredSpansConfiguration {
+ private final boolean enabled;
private final boolean profilerLoggingEnabled;
private final boolean backupDiagnosticFiles;
private final int asyncProfilerSafeMode;
@@ -30,6 +31,7 @@ public class InferredSpansConfiguration {
@SuppressWarnings("TooManyParameters")
public InferredSpansConfiguration(
+ boolean enabled,
boolean profilerLoggingEnabled,
boolean backupDiagnosticFiles,
int asyncProfilerSafeMode,
@@ -42,6 +44,7 @@ public InferredSpansConfiguration(
Duration profilingDuration,
@Nullable String profilerLibDirectory,
BiConsumer parentOverrideHandler) {
+ this.enabled = enabled;
this.profilerLoggingEnabled = profilerLoggingEnabled;
this.backupDiagnosticFiles = backupDiagnosticFiles;
this.asyncProfilerSafeMode = asyncProfilerSafeMode;
@@ -56,6 +59,10 @@ public InferredSpansConfiguration(
this.parentOverrideHandler = parentOverrideHandler;
}
+ public boolean isEnabled() {
+ return enabled;
+ }
+
public boolean isProfilingLoggingEnabled() {
return profilerLoggingEnabled;
}
diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfiler.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfiler.java
index 90a2bc250..2b5dff2b6 100644
--- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfiler.java
+++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfiler.java
@@ -150,6 +150,7 @@ public class SamplingProfiler implements Runnable {
private final ProfilingActivationListener activationListener;
private final Supplier tracerProvider;
+ @Nullable private final File tempDir;
private final AsyncProfiler profiler;
@Nullable private volatile Future> profilingTask;
@@ -171,9 +172,11 @@ public SamplingProfiler(
SpanAnchoredClock nanoClock,
Supplier tracerProvider,
@Nullable File activationEventsFile,
- @Nullable File jfrFile) {
+ @Nullable File jfrFile,
+ @Nullable File tempDir) {
this.config = config;
this.tracerProvider = tracerProvider;
+ this.tempDir = tempDir;
this.scheduler =
Executors.newSingleThreadScheduledExecutor(
r -> {
@@ -253,12 +256,13 @@ boolean isProfilingActiveOnThread(Thread thread) {
private synchronized void createFilesIfRequired() throws IOException {
if (jfrFile == null || !jfrFile.exists()) {
- jfrFile = File.createTempFile("otel-inferred-traces-", ".jfr");
+ jfrFile = File.createTempFile("otel-inferred-traces-", ".jfr", tempDir);
jfrFile.deleteOnExit();
canDeleteJfrFile = true;
}
if (activationEventsFile == null || !activationEventsFile.exists()) {
- activationEventsFile = File.createTempFile("otel-inferred-activation-events-", ".bin");
+ activationEventsFile =
+ File.createTempFile("otel-inferred-activation-events-", ".bin", tempDir);
activationEventsFile.deleteOnExit();
canDeleteActivationEventsFile = true;
}
@@ -353,6 +357,10 @@ public boolean onDeactivation(Span deactivatedSpan, @Nullable Span previouslyAct
@Override
@SuppressWarnings("FutureReturnValueIgnored")
public void run() {
+ if (!config.isEnabled()) {
+ logger.fine("Profiling is disabled, not starting profiling session");
+ return;
+ }
// lazily create temporary files
try {
diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfigTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfigTest.java
index b5ce0c650..a82422dc6 100644
--- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfigTest.java
+++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfigTest.java
@@ -56,20 +56,20 @@ public void checkAllOptions(@TempDir Path tmpDir) {
String libDir = tmpDir.resolve("foo").resolve("bar").toString();
try (AutoConfigTestProperties props =
new AutoConfigTestProperties()
- .put(InferredSpansAutoConfig.ENABLED_OPTION, "true")
- .put(InferredSpansAutoConfig.LOGGING_OPTION, "false")
- .put(InferredSpansAutoConfig.DIAGNOSTIC_FILES_OPTION, "true")
- .put(InferredSpansAutoConfig.SAFEMODE_OPTION, "16")
- .put(InferredSpansAutoConfig.POSTPROCESSING_OPTION, "false")
- .put(InferredSpansAutoConfig.SAMPLING_INTERVAL_OPTION, "7ms")
- .put(InferredSpansAutoConfig.MIN_DURATION_OPTION, "2ms")
- .put(InferredSpansAutoConfig.INCLUDED_CLASSES_OPTION, "foo*23,bar.baz")
- .put(InferredSpansAutoConfig.EXCLUDED_CLASSES_OPTION, "blub,test*.test2")
- .put(InferredSpansAutoConfig.INTERVAL_OPTION, "2s")
- .put(InferredSpansAutoConfig.DURATION_OPTION, "3s")
- .put(InferredSpansAutoConfig.LIB_DIRECTORY_OPTION, libDir)
+ .put(InferredSpansConfig.ENABLED_OPTION, "true")
+ .put(InferredSpansConfig.LOGGING_OPTION, "false")
+ .put(InferredSpansConfig.DIAGNOSTIC_FILES_OPTION, "true")
+ .put(InferredSpansConfig.SAFEMODE_OPTION, "16")
+ .put(InferredSpansConfig.POSTPROCESSING_OPTION, "false")
+ .put(InferredSpansConfig.SAMPLING_INTERVAL_OPTION, "7ms")
+ .put(InferredSpansConfig.MIN_DURATION_OPTION, "2ms")
+ .put(InferredSpansConfig.INCLUDED_CLASSES_OPTION, "foo*23,bar.baz")
+ .put(InferredSpansConfig.EXCLUDED_CLASSES_OPTION, "blub,test*.test2")
+ .put(InferredSpansConfig.INTERVAL_OPTION, "2s")
+ .put(InferredSpansConfig.DURATION_OPTION, "3s")
+ .put(InferredSpansConfig.LIB_DIRECTORY_OPTION, libDir)
.put(
- InferredSpansAutoConfig.PARENT_OVERRIDE_HANDLER_OPTION,
+ InferredSpansConfig.PARENT_OVERRIDE_HANDLER_OPTION,
NoOpParentOverrideHandler.class.getName())) {
OpenTelemetry otel = GlobalOpenTelemetry.get();
@@ -112,10 +112,10 @@ void checkDisabledbyDefault() {
void checkProfilerWorking() {
try (AutoConfigTestProperties props =
new AutoConfigTestProperties()
- .put(InferredSpansAutoConfig.ENABLED_OPTION, "true")
- .put(InferredSpansAutoConfig.DURATION_OPTION, "500ms")
- .put(InferredSpansAutoConfig.INTERVAL_OPTION, "500ms")
- .put(InferredSpansAutoConfig.SAMPLING_INTERVAL_OPTION, "5ms")) {
+ .put(InferredSpansConfig.ENABLED_OPTION, "true")
+ .put(InferredSpansConfig.DURATION_OPTION, "500ms")
+ .put(InferredSpansConfig.INTERVAL_OPTION, "500ms")
+ .put(InferredSpansConfig.SAMPLING_INTERVAL_OPTION, "5ms")) {
OpenTelemetry otel = GlobalOpenTelemetry.get();
List processors = OtelReflectionUtils.getSpanProcessors(otel);
assertThat(processors).filteredOn(proc -> proc instanceof InferredSpansProcessor).hasSize(1);
diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java
new file mode 100644
index 000000000..b33624561
--- /dev/null
+++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.contrib.inferredspans;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.condition.OS.WINDOWS;
+
+import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+
+@DisabledOnOs(WINDOWS) // Uses async-profiler, which is not supported on Windows
+class InferredSpansSpanProcessorProviderTest {
+
+ private ProfilerTestSetup setup;
+
+ @BeforeEach
+ void setUp() {
+ setup = ProfilerTestSetup.create(c -> {});
+ }
+
+ @AfterEach
+ void tearDown() {
+ if (setup != null) {
+ setup.close();
+ }
+ InferredSpans.setInstance(null);
+ }
+
+ @Test
+ void declarativeConfig() {
+ String yaml =
+ "file_format: 1.0-rc.1\n"
+ + "tracer_provider:\n"
+ + " processors:\n"
+ + " - inferred_spans/development:\n"
+ + " backup_diagnostic_files: true\n";
+
+ OpenTelemetrySdk sdk =
+ DeclarativeConfiguration.parseAndCreate(
+ new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)));
+
+ assertThat(sdk)
+ .extracting("tracerProvider")
+ .extracting("delegate")
+ .extracting("sharedState")
+ .extracting("activeSpanProcessor")
+ .extracting("profiler")
+ .extracting("config")
+ .extracting("backupDiagnosticFiles")
+ .isEqualTo(true);
+ }
+
+ @Test
+ void declarativeConfigDisabled() {
+ String yaml =
+ "file_format: 1.0-rc.1\n"
+ + "tracer_provider:\n"
+ + " processors:\n"
+ + " - inferred_spans/development:\n"
+ + " enabled: false\n";
+
+ OpenTelemetrySdk sdk =
+ DeclarativeConfiguration.parseAndCreate(
+ new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)));
+
+ assertThat(sdk)
+ .extracting("tracerProvider")
+ .extracting("delegate")
+ .extracting("sharedState")
+ .extracting("activeSpanProcessor")
+ .satisfies(
+ p ->
+ assertThat(p.getClass().getName())
+ .isEqualTo("io.opentelemetry.sdk.trace.NoopSpanProcessor"));
+ }
+}
diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfilerTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfilerTest.java
index b97ce8729..327077f85 100644
--- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfilerTest.java
+++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfilerTest.java
@@ -32,12 +32,12 @@
import java.util.stream.Stream;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.condition.OS;
+import org.junit.jupiter.api.io.TempDir;
// async-profiler doesn't work on Windows
@DisabledOnOs(OS.WINDOWS)
@@ -46,11 +46,7 @@ class SamplingProfilerTest {
private ProfilerTestSetup setup;
- @BeforeEach
- void setup() {
- // avoids any test failure to make other tests to fail
- getProfilerTempFiles().forEach(SamplingProfilerTest::silentDeleteFile);
- }
+ @TempDir private Path tempDir;
@AfterEach
void tearDown() {
@@ -58,14 +54,15 @@ void tearDown() {
setup.close();
setup = null;
}
- getProfilerTempFiles().forEach(SamplingProfilerTest::silentDeleteFile);
}
@Test
- void shouldLazilyCreateTempFilesAndCleanThem() throws Exception {
-
- List tempFiles = getProfilerTempFiles();
- assertThat(tempFiles).isEmpty();
+ void shouldLazilyCreateTempFilesAndCleanThem() {
+ for (Path file : getProfilerTempFiles()) {
+ if (!file.toFile().delete()) {
+ throw new IllegalStateException("Could not delete temp file: " + file);
+ }
+ }
// temporary files should be created on-demand, and properly deleted afterwards
setupProfiler(false);
@@ -117,8 +114,8 @@ void shouldNotDeleteProvidedFiles() throws Exception {
defaultConfig = ProfilerTestSetup.extractProfilerImpl(profiler1).getConfig();
}
- Path tempFile1 = Files.createTempFile("otel-inferred-provided", "test.bin");
- Path tempFile2 = Files.createTempFile("otel-inferred-provided", "test.jfr");
+ Path tempFile1 = Files.createTempFile(tempDir, "otel-inferred-provided", "test.bin");
+ Path tempFile2 = Files.createTempFile(tempDir, "otel-inferred-provided", "test.jfr");
try (OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build()) {
@@ -128,7 +125,8 @@ void shouldNotDeleteProvidedFiles() throws Exception {
new FixedClock(),
() -> sdk.getTracer("my-tracer"),
tempFile1.toFile(),
- tempFile2.toFile());
+ tempFile2.toFile(),
+ tempDir.toFile());
otherProfiler.start();
awaitProfilerStarted(otherProfiler);
@@ -341,12 +339,4 @@ private static void awaitProfilerStarted(SamplingProfiler profiler) {
.timeout(Duration.ofSeconds(6))
.until(() -> profiler.getProfilingSessions() > 1);
}
-
- private static void silentDeleteFile(Path f) {
- try {
- Files.delete(f);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
}