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> 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> 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); - } - } }