From 039f678facade2929bc864b1efaa80d342779c51 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 14 Jul 2025 18:51:19 +0200 Subject: [PATCH 01/36] add property translation for inferred spans --- inferred-spans/build.gradle.kts | 4 +++ .../InferredSpansCustomizerProvider.java | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts index 98d5e33a3..bc89d6da9 100644 --- a/inferred-spans/build.gradle.kts +++ b/inferred-spans/build.gradle.kts @@ -9,10 +9,13 @@ description = "OpenTelemetry Java profiling based inferred spans module" otelJava.moduleName.set("io.opentelemetry.contrib.inferredspans") dependencies { + implementation(project(":declarative-config-bridge")) + 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-incubator") compileOnly("io.opentelemetry.semconv:opentelemetry-semconv") implementation("com.lmax:disruptor") implementation("org.jctools:jctools-core") @@ -25,6 +28,7 @@ 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") diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java new file mode 100644 index 000000000..fa55f06e4 --- /dev/null +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.inferredspans; + +import com.google.auto.service.AutoService; +import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import java.util.Collections; + +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public class InferredSpansCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + ConfigProperties configProperties = ConfigPropertiesUtil.resolveModel(model, + Collections.singletonMap("otel.inferred.spans", "inferred_spans")); + + return model; + }); + } + +} From 54bd19369826b1440b8861325e96a04faedcb511 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 08:10:49 +0200 Subject: [PATCH 02/36] inferred spans --- .../InferredSpansAutoConfig.java | 56 +++++++++++-------- .../InferredSpansComponentProvider.java | 33 +++++++++++ .../InferredSpansCustomizerProvider.java | 19 ++++++- .../InferredSpansCustomizerProviderTest.java | 41 ++++++++++++++ 4 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java create mode 100644 inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java 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..d926da96d 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 @@ -45,30 +45,8 @@ public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvi 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()); + if (isEnabled(properties)) { + providerBuilder.addSpanProcessor(create(properties)); } else { log.finest( "Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set"); @@ -77,6 +55,36 @@ public void customize(AutoConfigurationCustomizer config) { }); } + static InferredSpansProcessor create(ConfigProperties properties) { + 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)); + } + + InferredSpansProcessor spanProcessor = builder.build(); + return spanProcessor; + } + + static boolean isEnabled(ConfigProperties properties) { + return properties.getBoolean(ENABLED_OPTION, false); + } + @SuppressWarnings("unchecked") private static BiConsumer constructParentOverrideHandler(String name) { try { diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java new file mode 100644 index 000000000..7563f3596 --- /dev/null +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -0,0 +1,33 @@ +/* + * 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.contrib.sdk.autoconfigure.ConfigPropertiesUtil; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; + +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class InferredSpansComponentProvider implements ComponentProvider { + @Override + public String getName() { + return "inferred_spans"; + } + + @Override + public SpanProcessor create(DeclarativeConfigProperties config) { + return InferredSpansAutoConfig.create( + ConfigPropertiesUtil.resolveInstrumentationConfig( + config, InferredSpansCustomizerProvider.TRANSLATION_MAP)); + } + + @Override + public Class getType() { + return SpanProcessor.class; + } +} diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java index fa55f06e4..744f9b9e4 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java @@ -10,20 +10,35 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel; import java.util.Collections; +import java.util.Map; @AutoService(DeclarativeConfigurationCustomizerProvider.class) public class InferredSpansCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { + static final Map TRANSLATION_MAP = + Collections.singletonMap("otel.inferred.spans", "inferred_spans"); + @Override public void customize(DeclarativeConfigurationCustomizer customizer) { customizer.addModelCustomizer( model -> { - ConfigProperties configProperties = ConfigPropertiesUtil.resolveModel(model, - Collections.singletonMap("otel.inferred.spans", "inferred_spans")); + ConfigProperties configProperties = + ConfigPropertiesUtil.resolveModel(model, TRANSLATION_MAP); + + TracerProviderModel tracerProvider = model.getTracerProvider(); + if (tracerProvider != null && InferredSpansAutoConfig.isEnabled(configProperties)) { + tracerProvider.getProcessors().add(create()); + } return model; }); } + @SuppressWarnings("NullAway") + private static SpanProcessorModel create() { + return new SpanProcessorModel().withAdditionalProperty("inferred_spans", null); + } } diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java new file mode 100644 index 000000000..3b83a923f --- /dev/null +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.inferredspans; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Test; + +class InferredSpansCustomizerProviderTest { + + @Test + void declarativeConfig() { + String yaml = + "file_format: 0.4\n" + + "tracer_provider:\n" + + "instrumentation/development:\n" + + " java:\n" + + " inferred_spans:\n" + + " enabled: true\n"; + + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + + new InferredSpansCustomizerProvider() + .customize( + c -> { + OpenTelemetryConfigurationModel configurationModel = c.apply(model); + assertThat(configurationModel.toString()) + .matches( + ".*SpanProcessorModel@.{8}\\[batch=,simple=,additionalProperties=\\{inferred_spans=null}.*"); + }); + } +} From 276565af2106b14d9acc1da66d32b010f0dd4391 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 10:02:24 +0200 Subject: [PATCH 03/36] inferred spans --- .../InferredSpansAutoConfig.java | 26 +++++----- .../InferredSpansComponentProvider.java | 7 ++- .../InferredSpansCustomizerProviderTest.java | 47 ++++++++++--------- 3 files changed, 43 insertions(+), 37 deletions(-) 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 d926da96d..4247c97f4 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 @@ -13,8 +13,10 @@ 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.trace.SpanProcessor; import java.time.Duration; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -45,17 +47,20 @@ public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvi public void customize(AutoConfigurationCustomizer config) { config.addTracerProviderCustomizer( (providerBuilder, properties) -> { - if (isEnabled(properties)) { - providerBuilder.addSpanProcessor(create(properties)); - } else { - log.finest( - "Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set"); - } + providerBuilder.addSpanProcessor(create(properties, /* enableByDefault= */ false)); return providerBuilder; }); } - static InferredSpansProcessor create(ConfigProperties properties) { + static SpanProcessor create(ConfigProperties properties, boolean enableByDefault) { + if (!properties.getBoolean(ENABLED_OPTION, enableByDefault)) { + log.finest( + "Not creating inferred spans processor because " + + ENABLED_OPTION + + " is not set to true"); + return SpanProcessor.composite(Collections.emptyList()); + } + InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder(); PropertiesApplier applier = new PropertiesApplier(properties); @@ -77,12 +82,7 @@ static InferredSpansProcessor create(ConfigProperties properties) { builder.parentOverrideHandler(constructParentOverrideHandler(parentOverrideHandlerName)); } - InferredSpansProcessor spanProcessor = builder.build(); - return spanProcessor; - } - - static boolean isEnabled(ConfigProperties properties) { - return properties.getBoolean(ENABLED_OPTION, false); + return builder.build(); } @SuppressWarnings("unchecked") diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 7563f3596..545bcc80c 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -10,10 +10,12 @@ import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.trace.SpanProcessor; +import java.util.Collections; @SuppressWarnings("rawtypes") @AutoService(ComponentProvider.class) public class InferredSpansComponentProvider implements ComponentProvider { + @Override public String getName() { return "inferred_spans"; @@ -22,8 +24,9 @@ public String getName() { @Override public SpanProcessor create(DeclarativeConfigProperties config) { return InferredSpansAutoConfig.create( - ConfigPropertiesUtil.resolveInstrumentationConfig( - config, InferredSpansCustomizerProvider.TRANSLATION_MAP)); + ConfigPropertiesUtil.resolveConfig( + config, Collections.singletonMap("otel.inferred.spans.", "")), + /* enableByDefault= */ true); } @Override diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index 3b83a923f..0e5cc85cd 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -5,37 +5,40 @@ package io.opentelemetry.contrib.inferredspans; -import static org.assertj.core.api.Assertions.assertThat; - import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import org.junit.jupiter.api.Test; + import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; -import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; class InferredSpansCustomizerProviderTest { + private static final String FQDN = + "spanProcessor=io.opentelemetry.contrib.inferredspans.InferredSpansProcessor"; + + @Test + void enabled() { + assertThat(create("")).contains(FQDN); + } + @Test - void declarativeConfig() { + void disabled() { + assertThat(create("enabled: false")).doesNotContain(FQDN); + } + + private static String create(String enabled) { String yaml = "file_format: 0.4\n" + "tracer_provider:\n" - + "instrumentation/development:\n" - + " java:\n" - + " inferred_spans:\n" - + " enabled: true\n"; - - OpenTelemetryConfigurationModel model = - DeclarativeConfiguration.parse( - new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); - - new InferredSpansCustomizerProvider() - .customize( - c -> { - OpenTelemetryConfigurationModel configurationModel = c.apply(model); - assertThat(configurationModel.toString()) - .matches( - ".*SpanProcessorModel@.{8}\\[batch=,simple=,additionalProperties=\\{inferred_spans=null}.*"); - }); + + " processors:\n" + + " - inferred_spans:\n" + + " " + + enabled + + "\n"; + return DeclarativeConfiguration.parseAndCreate( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) + .toString(); } } From 04b1e2b996eaee75ccde3f557997b49219a9be28 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 10:24:46 +0200 Subject: [PATCH 04/36] inferred spans --- .../InferredSpansAutoConfig.java | 18 +++----- .../InferredSpansComponentProvider.java | 3 +- .../InferredSpansCustomizerProvider.java | 44 ------------------- .../InferredSpansCustomizerProviderTest.java | 41 +++++++++-------- 4 files changed, 30 insertions(+), 76 deletions(-) delete mode 100644 inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java 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 4247c97f4..cb3bbbb88 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 @@ -16,7 +16,6 @@ import io.opentelemetry.sdk.trace.SpanProcessor; import java.time.Duration; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -47,20 +46,17 @@ public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvi public void customize(AutoConfigurationCustomizer config) { config.addTracerProviderCustomizer( (providerBuilder, properties) -> { - providerBuilder.addSpanProcessor(create(properties, /* enableByDefault= */ false)); + if (properties.getBoolean(ENABLED_OPTION, false)) { + providerBuilder.addSpanProcessor(create(properties)); + } else { + log.finest( + "Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set"); + } return providerBuilder; }); } - static SpanProcessor create(ConfigProperties properties, boolean enableByDefault) { - if (!properties.getBoolean(ENABLED_OPTION, enableByDefault)) { - log.finest( - "Not creating inferred spans processor because " - + ENABLED_OPTION - + " is not set to true"); - return SpanProcessor.composite(Collections.emptyList()); - } - + static SpanProcessor create(ConfigProperties properties) { InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder(); PropertiesApplier applier = new PropertiesApplier(properties); diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 545bcc80c..21101d639 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -25,8 +25,7 @@ public String getName() { public SpanProcessor create(DeclarativeConfigProperties config) { return InferredSpansAutoConfig.create( ConfigPropertiesUtil.resolveConfig( - config, Collections.singletonMap("otel.inferred.spans.", "")), - /* enableByDefault= */ true); + config, Collections.singletonMap("otel.inferred.spans.", ""))); } @Override diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java deleted file mode 100644 index 744f9b9e4..000000000 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProvider.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.contrib.inferredspans; - -import com.google.auto.service.AutoService; -import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel; -import java.util.Collections; -import java.util.Map; - -@AutoService(DeclarativeConfigurationCustomizerProvider.class) -public class InferredSpansCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { - - static final Map TRANSLATION_MAP = - Collections.singletonMap("otel.inferred.spans", "inferred_spans"); - - @Override - public void customize(DeclarativeConfigurationCustomizer customizer) { - customizer.addModelCustomizer( - model -> { - ConfigProperties configProperties = - ConfigPropertiesUtil.resolveModel(model, TRANSLATION_MAP); - - TracerProviderModel tracerProvider = model.getTracerProvider(); - if (tracerProvider != null && InferredSpansAutoConfig.isEnabled(configProperties)) { - tracerProvider.getProcessors().add(create()); - } - - return model; - }); - } - - @SuppressWarnings("NullAway") - private static SpanProcessorModel create() { - return new SpanProcessorModel().withAdditionalProperty("inferred_spans", null); - } -} diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index 0e5cc85cd..cca693e42 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.contrib.inferredspans; +import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; import org.junit.jupiter.api.Test; @@ -15,30 +16,32 @@ class InferredSpansCustomizerProviderTest { - private static final String FQDN = - "spanProcessor=io.opentelemetry.contrib.inferredspans.InferredSpansProcessor"; - - @Test - void enabled() { - assertThat(create("")).contains(FQDN); - } - @Test - void disabled() { - assertThat(create("enabled: false")).doesNotContain(FQDN); - } - - private static String create(String enabled) { + void declarativeConfig() { String yaml = "file_format: 0.4\n" + "tracer_provider:\n" + " processors:\n" + " - inferred_spans:\n" - + " " - + enabled - + "\n"; - return DeclarativeConfiguration.parseAndCreate( - new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) - .toString(); + + " backup:\n" + + " diagnostic:\n" + + " 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); + + assertThat(sdk.toString()) + .contains("spanProcessor=io.opentelemetry.contrib.inferredspans.InferredSpansProcessor"); } } From c622bd4a2a086bb33a3ea7b181f60c2d0b3943fd Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 10:55:17 +0200 Subject: [PATCH 05/36] format --- .../inferredspans/InferredSpansCustomizerProviderTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index cca693e42..20685cbeb 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -5,14 +5,13 @@ package io.opentelemetry.contrib.inferredspans; +import static org.assertj.core.api.Assertions.assertThat; + import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; -import org.junit.jupiter.api.Test; - import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; class InferredSpansCustomizerProviderTest { From 00c4eb500ee6c8c8f006bab6d699568b14af5700 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 13:36:43 +0200 Subject: [PATCH 06/36] fix --- .../processor/BaggageProcessorCustomizerTest.java | 4 ++-- .../InferredSpansCustomizerProviderTest.java | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java index 645ff5334..d3f9f8c5b 100644 --- a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java +++ b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java @@ -107,9 +107,9 @@ private static OpenTelemetrySdk getOpenTelemetrySdk( // We set the export interval of the spans to 10 ms. The default value is 5 // seconds. "otel.bsp.schedule.delay", // span exporter - "10", + "100", "otel.blrp.schedule.delay", // log exporter - "10", + "100", "otel.traces.exporter", MEMORY_EXPORTER, "otel.metrics.exporter", diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index 20685cbeb..ea1665d70 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -7,14 +7,25 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.opentelemetry.contrib.inferredspans.internal.ProfilingActivationListener; +import io.opentelemetry.contrib.inferredspans.internal.util.OtelReflectionUtils; 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; class InferredSpansCustomizerProviderTest { + @BeforeEach + @AfterEach + public void resetGlobalOtel() { + ProfilingActivationListener.ensureInitialized(); + OtelReflectionUtils.shutdownAndResetGlobalOtel(); + } + @Test void declarativeConfig() { String yaml = @@ -39,8 +50,5 @@ void declarativeConfig() { .extracting("config") .extracting("backupDiagnosticFiles") .isEqualTo(true); - - assertThat(sdk.toString()) - .contains("spanProcessor=io.opentelemetry.contrib.inferredspans.InferredSpansProcessor"); } } From 135cacd8281c1344c5fedc0be1387980f31a4437 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 14:00:40 +0200 Subject: [PATCH 07/36] make temp dir more reliable --- .../inferredspans/InferredSpansProcessor.java | 3 +- .../internal/SamplingProfiler.java | 10 ++++-- .../internal/SamplingProfilerTest.java | 32 ++++--------------- 3 files changed, 15 insertions(+), 30 deletions(-) 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/internal/SamplingProfiler.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/internal/SamplingProfiler.java index 90a2bc250..b221cfe50 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; } 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..ff4a8def5 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,7 +54,6 @@ void tearDown() { setup.close(); setup = null; } - getProfilerTempFiles().forEach(SamplingProfilerTest::silentDeleteFile); } @Test @@ -117,8 +112,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 +123,8 @@ void shouldNotDeleteProvidedFiles() throws Exception { new FixedClock(), () -> sdk.getTracer("my-tracer"), tempFile1.toFile(), - tempFile2.toFile()); + tempFile2.toFile(), + tempDir.toFile()); otherProfiler.start(); awaitProfilerStarted(otherProfiler); @@ -333,20 +329,4 @@ private void setupProfiler(Consumer configCustomi configCustomizer.accept(config); }); } - - private static void awaitProfilerStarted(SamplingProfiler profiler) { - // ensure profiler is initialized - await() - .pollDelay(Duration.ofMillis(10)) - .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); - } - } } From f410aacc77a69fec96bbdeb818bd11cf41b645b7 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 14:05:17 +0200 Subject: [PATCH 08/36] make temp dir more reliable --- .../inferredspans/internal/SamplingProfilerTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 ff4a8def5..1e9d97807 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 @@ -329,4 +329,12 @@ private void setupProfiler(Consumer configCustomi configCustomizer.accept(config); }); } + + private static void awaitProfilerStarted(SamplingProfiler profiler) { + // ensure profiler is initialized + await() + .pollDelay(Duration.ofMillis(10)) + .timeout(Duration.ofSeconds(6)) + .until(() -> profiler.getProfilingSessions() > 1); + } } From 06039b6cf0c14c6b01ba4e73b005edc2731896f2 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 14:19:42 +0200 Subject: [PATCH 09/36] test starts async profiler (?) --- .../InferredSpansCustomizerProviderTest.java | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java deleted file mode 100644 index ea1665d70..000000000 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.contrib.inferredspans; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.opentelemetry.contrib.inferredspans.internal.ProfilingActivationListener; -import io.opentelemetry.contrib.inferredspans.internal.util.OtelReflectionUtils; -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; - -class InferredSpansCustomizerProviderTest { - - @BeforeEach - @AfterEach - public void resetGlobalOtel() { - ProfilingActivationListener.ensureInitialized(); - OtelReflectionUtils.shutdownAndResetGlobalOtel(); - } - - @Test - void declarativeConfig() { - String yaml = - "file_format: 0.4\n" - + "tracer_provider:\n" - + " processors:\n" - + " - inferred_spans:\n" - + " backup:\n" - + " diagnostic:\n" - + " 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); - } -} From c2f1506a50d8f7eff6baf264672a68372efc7034 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 14:37:01 +0200 Subject: [PATCH 10/36] Revert "test starts async profiler (?)" This reverts commit 26ce15989056d8d9583bb5acc2434a8a73863226. --- .../InferredSpansCustomizerProviderTest.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java new file mode 100644 index 000000000..ea1665d70 --- /dev/null +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.inferredspans; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.contrib.inferredspans.internal.ProfilingActivationListener; +import io.opentelemetry.contrib.inferredspans.internal.util.OtelReflectionUtils; +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; + +class InferredSpansCustomizerProviderTest { + + @BeforeEach + @AfterEach + public void resetGlobalOtel() { + ProfilingActivationListener.ensureInitialized(); + OtelReflectionUtils.shutdownAndResetGlobalOtel(); + } + + @Test + void declarativeConfig() { + String yaml = + "file_format: 0.4\n" + + "tracer_provider:\n" + + " processors:\n" + + " - inferred_spans:\n" + + " backup:\n" + + " diagnostic:\n" + + " 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); + } +} From d91072b2ac0498da2324eb1d51ba22a40ec0853f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 14:42:15 +0200 Subject: [PATCH 11/36] leave profiler disabled in test --- .../contrib/inferredspans/InferredSpansAutoConfig.java | 1 + .../inferredspans/InferredSpansProcessorBuilder.java | 7 +++++++ .../inferredspans/internal/InferredSpansConfiguration.java | 7 +++++++ .../contrib/inferredspans/internal/SamplingProfiler.java | 4 ++++ .../inferredspans/InferredSpansCustomizerProviderTest.java | 1 + 5 files changed, 20 insertions(+) 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 cb3bbbb88..871c087de 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 @@ -61,6 +61,7 @@ static SpanProcessor create(ConfigProperties properties) { PropertiesApplier applier = new PropertiesApplier(properties); + applier.applyBool(ENABLED_OPTION, builder::profilerEnabled); applier.applyBool(LOGGING_OPTION, builder::profilerLoggingEnabled); applier.applyBool(DIAGNOSTIC_FILES_OPTION, builder::backupDiagnosticFiles); applier.applyInt(SAFEMODE_OPTION, builder::asyncProfilerSafeMode); 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/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 b221cfe50..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 @@ -357,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/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index ea1665d70..6f86324b3 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -33,6 +33,7 @@ void declarativeConfig() { + "tracer_provider:\n" + " processors:\n" + " - inferred_spans:\n" + + " enabled: false\n" + " backup:\n" + " diagnostic:\n" + " files: true\n"; From a297c8e774fa4a128a0e55df9514f6ba7dcb5902 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 15 Jul 2025 14:48:49 +0200 Subject: [PATCH 12/36] leave profiler disabled in test --- .../inferredspans/InferredSpansCustomizerProviderTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index 6f86324b3..aae74fb00 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -16,7 +16,10 @@ 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; +import org.junit.jupiter.api.condition.OS; +@DisabledOnOs(OS.WINDOWS) class InferredSpansCustomizerProviderTest { @BeforeEach From 60728e56cf37c5e6138edbdee0e6afc6a8fba26f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 15 Aug 2025 13:35:16 +0200 Subject: [PATCH 13/36] copy declarative config from agent --- .../BaggageProcessorCustomizerTest.java | 4 +- declarative-config-bridge/build.gradle.kts | 18 ++ .../DeclarativeConfigPropertiesBridge.java | 188 ++++++++++++++++++ ...larativeConfigPropertiesBridgeBuilder.java | 104 ++++++++++ ...tiveConfigPropertiesBridgeBuilderTest.java | 90 +++++++++ ...DeclarativeConfigPropertiesBridgeTest.java | 144 ++++++++++++++ .../src/test/resources/config.yaml | 27 +++ inferred-spans/build.gradle.kts | 2 +- .../InferredSpansComponentProvider.java | 9 +- settings.gradle.kts | 1 + 10 files changed, 580 insertions(+), 7 deletions(-) create mode 100644 declarative-config-bridge/build.gradle.kts create mode 100644 declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java create mode 100644 declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java create mode 100644 declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java create mode 100644 declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java create mode 100644 declarative-config-bridge/src/test/resources/config.yaml diff --git a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java index d3f9f8c5b..645ff5334 100644 --- a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java +++ b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageProcessorCustomizerTest.java @@ -107,9 +107,9 @@ private static OpenTelemetrySdk getOpenTelemetrySdk( // We set the export interval of the spans to 10 ms. The default value is 5 // seconds. "otel.bsp.schedule.delay", // span exporter - "100", + "10", "otel.blrp.schedule.delay", // log exporter - "100", + "10", "otel.traces.exporter", MEMORY_EXPORTER, "otel.metrics.exporter", diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts new file mode 100644 index 000000000..1da7432d0 --- /dev/null +++ b/declarative-config-bridge/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("otel.java-conventions") + id("otel.publish-conventions") +} + +description = "OpenTelemetry extension that provides a bridge for declarative configuration." +otelJava.moduleName.set("io.opentelemetry.contrib.sdk.declarative.config.bridge") + +dependencies { + // We use `compileOnly` dependency because during runtime all necessary classes are provided by + // javaagent itself. + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") + + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") + testImplementation("org.mockito:mockito-inline") +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java new file mode 100644 index 000000000..bc7f4b117 --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java @@ -0,0 +1,188 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; +import javax.annotation.Nullable; + +/** + * A {@link ConfigProperties} which resolves properties based on {@link + * DeclarativeConfigProperties}. + * + *

Only properties starting with "otel.instrumentation." are resolved. Others return null (or + * default value if provided). + * + *

To resolve: + * + *

    + *
  • "otel.instrumentation" refers to the ".instrumentation.java" node + *
  • The portion of the property after "otel.instrumentation." is split into segments based on + * ".". + *
  • For each N-1 segment, we walk down the tree to find the relevant leaf {@link + * DeclarativeConfigProperties}. + *
  • We extract the property from the resolved {@link DeclarativeConfigProperties} using the + * last segment as the property key. + *
+ * + *

For example, given the following YAML, asking for {@code + * ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value": + * + *

+ *   instrumentation:
+ *     java:
+ *       common:
+ *         string_key: value
+ * 
+ */ +final class DeclarativeConfigPropertiesBridge implements ConfigProperties { + + private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; + + private final DeclarativeConfigProperties baseNode; + + // lookup order matters - we choose the first match + private final Map mappings; + private final Map overrideValues; + + DeclarativeConfigPropertiesBridge( + DeclarativeConfigProperties baseNode, + Map mappings, + Map overrideValues) { + this.baseNode = Objects.requireNonNull(baseNode); + this.mappings = mappings; + this.overrideValues = overrideValues; + } + + @Nullable + @Override + public String getString(String propertyName) { + return getPropertyValue(propertyName, String.class, DeclarativeConfigProperties::getString); + } + + @Nullable + @Override + public Boolean getBoolean(String propertyName) { + return getPropertyValue(propertyName, Boolean.class, DeclarativeConfigProperties::getBoolean); + } + + @Nullable + @Override + public Integer getInt(String propertyName) { + return getPropertyValue(propertyName, Integer.class, DeclarativeConfigProperties::getInt); + } + + @Nullable + @Override + public Long getLong(String propertyName) { + return getPropertyValue(propertyName, Long.class, DeclarativeConfigProperties::getLong); + } + + @Nullable + @Override + public Double getDouble(String propertyName) { + return getPropertyValue(propertyName, Double.class, DeclarativeConfigProperties::getDouble); + } + + @Nullable + @Override + public Duration getDuration(String propertyName) { + Long millis = getPropertyValue(propertyName, Long.class, DeclarativeConfigProperties::getLong); + if (millis == null) { + return null; + } + return Duration.ofMillis(millis); + } + + @SuppressWarnings("unchecked") + @Override + public List getList(String propertyName) { + List propertyValue = + getPropertyValue( + propertyName, + List.class, + (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); + return propertyValue == null ? Collections.emptyList() : propertyValue; + } + + @SuppressWarnings("unchecked") + @Override + public Map getMap(String propertyName) { + DeclarativeConfigProperties propertyValue = + getPropertyValue( + propertyName, + DeclarativeConfigProperties.class, + DeclarativeConfigProperties::getStructured); + if (propertyValue == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + propertyValue + .getPropertyKeys() + .forEach( + key -> { + String value = propertyValue.getString(key); + if (value == null) { + return; + } + result.put(key, value); + }); + return Collections.unmodifiableMap(result); + } + + @Nullable + private T getPropertyValue( + String property, + Class clazz, + BiFunction extractor) { + T override = clazz.cast(overrideValues.get(property)); + if (override != null) { + return override; + } + + String[] segments = getSegments(translateProperty(property)); + if (segments.length == 0) { + return null; + } + + // Extract the value by walking to the N-1 entry + DeclarativeConfigProperties target = baseNode; + if (segments.length > 1) { + for (int i = 0; i < segments.length - 1; i++) { + target = target.getStructured(segments[i], empty()); + } + } + String lastPart = segments[segments.length - 1]; + + return extractor.apply(target, lastPart); + } + + static String[] getSegments(String property) { + if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { + property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); + } + // Split the remainder of the property on "." + return property.replace('-', '_').split("\\."); + } + + private String translateProperty(String property) { + for (Map.Entry entry : mappings.entrySet()) { + if (property.startsWith(entry.getKey())) { + return entry.getValue() + property.substring(entry.getKey().length()); + } + } + return property; + } +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java new file mode 100644 index 000000000..757e205a6 --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java @@ -0,0 +1,104 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.annotation.Nullable; + +/** + * A builder for {@link DeclarativeConfigPropertiesBridge} that allows adding translations and fixed + * values for properties. + */ +public class DeclarativeConfigPropertiesBridgeBuilder { + /** + * order is important here, so we use LinkedHashMap - see {@link #addMapping(String, String)} for + * more details + */ + private final Map mappings = new LinkedHashMap<>(); + + private final Map overrideValues = new HashMap<>(); + + public DeclarativeConfigPropertiesBridgeBuilder() {} + + /** + * Adds a mapping from a property prefix to a YAML path. + * + *

For example, if the property prefix is "otel.javaagent" and the YAML path is "agent", then + * any property starting with "otel.javaagent." will be resolved against the "agent" node in the + * instrumentation/java section of the YAML configuration. + * + * @param propertyPrefix the prefix of the property to translate + * @param yamlPath the YAML path to resolve the property against + */ + @CanIgnoreReturnValue + public DeclarativeConfigPropertiesBridgeBuilder addMapping( + String propertyPrefix, String yamlPath) { + mappings.put(propertyPrefix, yamlPath); + return this; + } + + /** + * Adds a fixed override value for a property. + * + * @param propertyName the name of the property to override + * @param value the value to return when the property is requested + */ + @CanIgnoreReturnValue + public DeclarativeConfigPropertiesBridgeBuilder addOverride(String propertyName, Object value) { + overrideValues.put(propertyName, value); + return this; + } + + /** Build {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */ + public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + ConfigProperties sdkConfigProperties = + AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); + if (sdkConfigProperties != null) { + return sdkConfigProperties; + } + ConfigProvider configProvider = + AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk); + if (configProvider != null) { + return buildFromInstrumentationConfig(configProvider.getInstrumentationConfig()); + } + // Should never happen + throw new IllegalStateException( + "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); + } + + /** + * Build {@link DeclarativeConfigPropertiesBridge} from the {@link DeclarativeConfigProperties}. + * + * @param node the declarative config properties to build from + * @return a new instance of {@link ConfigProperties} + */ + public ConfigProperties build(@Nullable DeclarativeConfigProperties node) { + return new DeclarativeConfigPropertiesBridge( + node == null ? empty() : node, mappings, overrideValues); + } + + /** + * Build {@link ConfigProperties} from the {@link DeclarativeConfigProperties}. + * + * @param instrumentationConfig the instrumentation configuration to build from + * @return a new instance of {@link ConfigProperties} + */ + public ConfigProperties buildFromInstrumentationConfig( + @Nullable DeclarativeConfigProperties instrumentationConfig) { + return build( + instrumentationConfig == null ? null : instrumentationConfig.getStructured("java")); + } +} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java new file mode 100644 index 000000000..e07a7fbf7 --- /dev/null +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +@SuppressWarnings("DoNotMockAutoValue") +class DeclarativeConfigPropertiesBridgeBuilderTest { + @Test + void shouldUseConfigPropertiesForAutoConfiguration() { + ConfigProperties configPropertiesMock = mock(ConfigProperties.class); + AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); + try (MockedStatic autoConfigureUtilMock = + Mockito.mockStatic(AutoConfigureUtil.class)) { + autoConfigureUtilMock + .when(() -> AutoConfigureUtil.getConfig(sdkMock)) + .thenReturn(configPropertiesMock); + + ConfigProperties configProperties = + new DeclarativeConfigPropertiesBridgeBuilder().build(sdkMock); + + assertThat(configProperties).isSameAs(configPropertiesMock); + } + } + + @Test + void shouldUseConfigProviderForDeclarativeConfiguration() { + String propertyName = "testProperty"; + String expectedValue = "the value"; + DeclarativeConfigProperties javaNodeMock = mock(DeclarativeConfigProperties.class); + when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue); + + DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class); + when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock); + + ConfigProvider configProviderMock = mock(ConfigProvider.class); + when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock); + + AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); + + try (MockedStatic autoConfigureUtilMock = + Mockito.mockStatic(AutoConfigureUtil.class)) { + autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null); + autoConfigureUtilMock + .when(() -> AutoConfigureUtil.getConfigProvider(sdkMock)) + .thenReturn(configProviderMock); + + ConfigProperties configProperties = + new DeclarativeConfigPropertiesBridgeBuilder().build(sdkMock); + + assertThat(configProperties.getString(propertyName)).isEqualTo(expectedValue); + } + } + + @Test + void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig() { + AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); + ConfigProvider configProviderMock = mock(ConfigProvider.class); + when(configProviderMock.getInstrumentationConfig()).thenReturn(null); + + try (MockedStatic autoConfigureUtilMock = + Mockito.mockStatic(AutoConfigureUtil.class)) { + autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null); + autoConfigureUtilMock + .when(() -> AutoConfigureUtil.getConfigProvider(sdkMock)) + .thenReturn(configProviderMock); + + ConfigProperties configProperties = + new DeclarativeConfigPropertiesBridgeBuilder().build(sdkMock); + + assertThat(configProperties.getString("testProperty")).isNull(); + } + } +} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java new file mode 100644 index 000000000..405cecc1c --- /dev/null +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -0,0 +1,144 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.sdk.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.InstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class DeclarativeConfigPropertiesBridgeTest { + + private ConfigProperties bridge; + private ConfigProperties emptyBridge; + + @BeforeEach + void setup() { + bridge = create(new DeclarativeConfigPropertiesBridgeBuilder()); + + OpenTelemetryConfigurationModel emptyModel = + new OpenTelemetryConfigurationModel() + .withAdditionalProperty("instrumentation/development", new InstrumentationModel()); + SdkConfigProvider emptyConfigProvider = SdkConfigProvider.create(emptyModel); + emptyBridge = + new DeclarativeConfigPropertiesBridgeBuilder() + .buildFromInstrumentationConfig( + Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig())); + } + + private static ConfigProperties create(DeclarativeConfigPropertiesBridgeBuilder builder) { + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse( + DeclarativeConfigPropertiesBridgeTest.class + .getClassLoader() + .getResourceAsStream("config.yaml")); + return builder.buildFromInstrumentationConfig( + SdkConfigProvider.create(model).getInstrumentationConfig()); + } + + @Test + void getProperties() { + // only properties starting with "otel.instrumentation." are resolved + // asking for properties which don't exist or inaccessible shouldn't result in an error + assertThat(bridge.getString("file_format")).isNull(); + assertThat(bridge.getString("file_format", "foo")).isEqualTo("foo"); + assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled")).isNull(); + assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled", true)) + .isTrue(); + + // common cases + assertThat(bridge.getBoolean("otel.instrumentation.runtime-telemetry.enabled")).isFalse(); + + // check all the types + Map expectedMap = new HashMap<>(); + expectedMap.put("string_key1", "value1"); + expectedMap.put("string_key2", "value2"); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.string_key")) + .isEqualTo("value"); + assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.bool_key")).isTrue(); + assertThat(bridge.getInt("otel.instrumentation.example-instrumentation.int_key")).isEqualTo(1); + assertThat(bridge.getLong("otel.instrumentation.example-instrumentation.int_key")) + .isEqualTo(1L); + assertThat(bridge.getDuration("otel.instrumentation.example-instrumentation.int_key")) + .isEqualTo(Duration.ofMillis(1)); + assertThat(bridge.getDouble("otel.instrumentation.example-instrumentation.double_key")) + .isEqualTo(1.1); + assertThat(bridge.getList("otel.instrumentation.example-instrumentation.list_key")) + .isEqualTo(Arrays.asList("value1", "value2")); + assertThat(bridge.getMap("otel.instrumentation.example-instrumentation.map_key")) + .isEqualTo(expectedMap); + + // asking for properties with the wrong type returns null + assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.string_key")) + .isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.bool_key")).isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.int_key")).isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.double_key")) + .isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.list_key")).isNull(); + assertThat(bridge.getString("otel.instrumentation.example-instrumentation.map_key")).isNull(); + + // check all the types + assertThat(bridge.getString("otel.instrumentation.other-instrumentation.string_key", "value")) + .isEqualTo("value"); + assertThat(bridge.getBoolean("otel.instrumentation.other-instrumentation.bool_key", true)) + .isTrue(); + assertThat(bridge.getInt("otel.instrumentation.other-instrumentation.int_key", 1)).isEqualTo(1); + assertThat(bridge.getLong("otel.instrumentation.other-instrumentation.int_key", 1L)) + .isEqualTo(1L); + assertThat( + bridge.getDuration( + "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) + .isEqualTo(Duration.ofMillis(1)); + assertThat(bridge.getDouble("otel.instrumentation.other-instrumentation.double_key", 1.1)) + .isEqualTo(1.1); + assertThat( + bridge.getList( + "otel.instrumentation.other-instrumentation.list_key", + Arrays.asList("value1", "value2"))) + .isEqualTo(Arrays.asList("value1", "value2")); + assertThat(bridge.getMap("otel.instrumentation.other-instrumentation.map_key", expectedMap)) + .isEqualTo(expectedMap); + } + + @Test + void vendor() { + // verify vendor specific property names are preserved in unchanged form (prefix is not stripped + // as for otel.instrumentation.*) + assertThat(bridge.getBoolean("acme.full_name.preserved")).isTrue(); + } + + @Test + void vendorTranslation() { + ConfigProperties propertiesBridge = + create(new DeclarativeConfigPropertiesBridgeBuilder().addMapping("acme", "acme.full_name")); + assertThat(propertiesBridge.getBoolean("acme.preserved")).isTrue(); + } + + @Test + void agentTranslation() { + ConfigProperties bridge = + create( + new DeclarativeConfigPropertiesBridgeBuilder() + .addMapping("otel.javaagent", "agent") + .addOverride("otel.javaagent.debug", true) + .addOverride("otel.javaagent.logging", "application")); + + assertThat(bridge.getBoolean("otel.javaagent.debug")).isTrue(); + assertThat(bridge.getBoolean("otel.javaagent.experimental.indy")).isTrue(); + assertThat(bridge.getString("otel.javaagent.logging")).isEqualTo("application"); + } +} diff --git a/declarative-config-bridge/src/test/resources/config.yaml b/declarative-config-bridge/src/test/resources/config.yaml new file mode 100644 index 000000000..ef0ce9a8c --- /dev/null +++ b/declarative-config-bridge/src/test/resources/config.yaml @@ -0,0 +1,27 @@ +file_format: 0.4 +instrumentation/development: + java: + acme: + full_name: + preserved: true + agent: + experimental: + indy: true + common: + default: + enabled: false + runtime_telemetry: + enabled: false + example_instrumentation: + string_key: value + bool_key: true + int_key: 1 + double_key: 1.1 + list_key: + - value1 + - value2 + - true + map_key: + string_key1: value1 + string_key2: value2 + bool_key: true diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts index bc89d6da9..bdf94a906 100644 --- a/inferred-spans/build.gradle.kts +++ b/inferred-spans/build.gradle.kts @@ -14,7 +14,7 @@ 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.semconv:opentelemetry-semconv") implementation("com.lmax:disruptor") diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 21101d639..19d8cf23c 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -7,10 +7,9 @@ import com.google.auto.service.AutoService; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.contrib.sdk.autoconfigure.ConfigPropertiesUtil; +import io.opentelemetry.contrib.sdk.autoconfigure.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.trace.SpanProcessor; -import java.util.Collections; @SuppressWarnings("rawtypes") @AutoService(ComponentProvider.class) @@ -24,8 +23,10 @@ public String getName() { @Override public SpanProcessor create(DeclarativeConfigProperties config) { return InferredSpansAutoConfig.create( - ConfigPropertiesUtil.resolveConfig( - config, Collections.singletonMap("otel.inferred.spans.", ""))); + new DeclarativeConfigPropertiesBridgeBuilder() + // crop the prefix, because the properties are under the "inferred_spans" processor + .addMapping("otel.inferred.spans.", "") + .build(config)); } @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index 01ea45838..f812b25c5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -80,6 +80,7 @@ include(":cel-sampler") include(":compressors:compressor-zstd") include(":cloudfoundry-resources") include(":consistent-sampling") +include(":declarative-config-bridge") include(":dependencyManagement") include(":disk-buffering") include(":ibm-mq-metrics") From 67d599faa5bcc18cf6a1457a3d32a0b47054efcb Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 15 Aug 2025 13:48:26 +0200 Subject: [PATCH 14/36] fix --- .../DeclarativeConfigPropertiesBridgeBuilderTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java index e07a7fbf7..f287f9dbd 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java @@ -6,7 +6,6 @@ package io.opentelemetry.contrib.sdk.autoconfigure; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -47,7 +46,7 @@ void shouldUseConfigProviderForDeclarativeConfiguration() { when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue); DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class); - when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock); + when(instrumentationConfigMock.getStructured(eq("java"))).thenReturn(javaNodeMock); ConfigProvider configProviderMock = mock(ConfigProvider.class); when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock); From e49832b230a289d2d0e9fb1dfc8dbafc85b94223 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 18 Aug 2025 15:21:39 +0200 Subject: [PATCH 15/36] fix package --- declarative-config-bridge/build.gradle.kts | 2 +- .../bridge}/DeclarativeConfigPropertiesBridge.java | 2 +- .../bridge}/DeclarativeConfigPropertiesBridgeBuilder.java | 2 +- .../DeclarativeConfigPropertiesBridgeBuilderTest.java | 1 + .../autoconfigure/DeclarativeConfigPropertiesBridgeTest.java | 1 + .../contrib/inferredspans/InferredSpansComponentProvider.java | 2 +- 6 files changed, 6 insertions(+), 4 deletions(-) rename declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/{autoconfigure => config/bridge}/DeclarativeConfigPropertiesBridge.java (99%) rename declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/{autoconfigure => config/bridge}/DeclarativeConfigPropertiesBridgeBuilder.java (98%) diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts index 1da7432d0..8e20094b4 100644 --- a/declarative-config-bridge/build.gradle.kts +++ b/declarative-config-bridge/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } description = "OpenTelemetry extension that provides a bridge for declarative configuration." -otelJava.moduleName.set("io.opentelemetry.contrib.sdk.declarative.config.bridge") +otelJava.moduleName.set("io.opentelemetry.contrib.sdk.config.bridge") dependencies { // We use `compileOnly` dependency because during runtime all necessary classes are provided by diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java similarity index 99% rename from declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java rename to declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java index bc7f4b117..c5384097c 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridge.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.contrib.sdk.autoconfigure; +package io.opentelemetry.contrib.sdk.config.bridge; import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java similarity index 98% rename from declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java rename to declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java index 757e205a6..7f00bdd5f 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilder.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.contrib.sdk.autoconfigure; +package io.opentelemetry.contrib.sdk.config.bridge; import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java index f287f9dbd..a353a307f 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.contrib.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java index 405cecc1c..ac08e221b 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java @@ -7,6 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.opentelemetry.contrib.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 19d8cf23c..9fe7ea1b6 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -7,7 +7,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.contrib.sdk.autoconfigure.DeclarativeConfigPropertiesBridgeBuilder; +import io.opentelemetry.contrib.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.trace.SpanProcessor; From 2e90880807c6b1bde4fbf63bc229f4d75607cb03 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 18 Aug 2025 15:22:56 +0200 Subject: [PATCH 16/36] update file format --- declarative-config-bridge/src/test/resources/config.yaml | 2 +- .../inferredspans/InferredSpansCustomizerProviderTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/declarative-config-bridge/src/test/resources/config.yaml b/declarative-config-bridge/src/test/resources/config.yaml index ef0ce9a8c..6c8d7fddc 100644 --- a/declarative-config-bridge/src/test/resources/config.yaml +++ b/declarative-config-bridge/src/test/resources/config.yaml @@ -1,4 +1,4 @@ -file_format: 0.4 +file_format: 1.0-rc.1 instrumentation/development: java: acme: diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index aae74fb00..8147e08aa 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -32,7 +32,7 @@ public void resetGlobalOtel() { @Test void declarativeConfig() { String yaml = - "file_format: 0.4\n" + "file_format: 1.0-rc.1\n" + "tracer_provider:\n" + " processors:\n" + " - inferred_spans:\n" From 6ccd8ee3dfb88a71668871a14b7e8319fde4ea8d Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 18 Aug 2025 15:25:26 +0200 Subject: [PATCH 17/36] add comment --- .../inferredspans/InferredSpansCustomizerProviderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index 8147e08aa..45f61b1f8 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -6,6 +6,7 @@ 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.contrib.inferredspans.internal.ProfilingActivationListener; import io.opentelemetry.contrib.inferredspans.internal.util.OtelReflectionUtils; @@ -17,9 +18,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; -@DisabledOnOs(OS.WINDOWS) +@DisabledOnOs(WINDOWS) // Uses async-profiler, which is not supported on Windows class InferredSpansCustomizerProviderTest { @BeforeEach From 5873f2e9fabeb5c7e9b0de9a96d0ac829807ae75 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 19 Aug 2025 14:24:25 +0200 Subject: [PATCH 18/36] extract common class --- .../InferredSpansAutoConfig.java | 110 +---------------- .../inferredspans/InferredSpansConfig.java | 111 ++++++++++++++++++ 2 files changed, 113 insertions(+), 108 deletions(-) create mode 100644 inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java 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 871c087de..ff71dfba5 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,49 +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 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 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)) { - providerBuilder.addSpanProcessor(create(properties)); + providerBuilder.addSpanProcessor(InferredSpansConfig.create(properties)); } else { log.finest( "Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set"); @@ -55,85 +30,4 @@ public void customize(AutoConfigurationCustomizer config) { return providerBuilder; }); } - - static SpanProcessor create(ConfigProperties properties) { - InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder(); - - PropertiesApplier applier = new PropertiesApplier(properties); - - applier.applyBool(ENABLED_OPTION, builder::profilerEnabled); - 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/InferredSpansConfig.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java new file mode 100644 index 000000000..4a2b26491 --- /dev/null +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java @@ -0,0 +1,111 @@ +package io.opentelemetry.contrib.inferredspans; + +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 java.util.stream.Collectors; +import javax.annotation.Nullable; + +public class 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 SpanProcessor create(ConfigProperties properties) { + InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder(); + + PropertiesApplier applier = new PropertiesApplier(properties); + + applier.applyBool(ENABLED_OPTION, builder::profilerEnabled); + 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(Collectors.toList()); + if (!values.isEmpty()) { + funcToApply.accept(values); + } + } + } + + private static void applyValue(@Nullable T value, Consumer funcToApply) { + if (value != null) { + funcToApply.accept(value); + } + } + } +} From a5d2d604c7c4b50bf68e7e5aff8fa52af0bdde40 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 19 Aug 2025 14:33:54 +0200 Subject: [PATCH 19/36] add description and owner of the declarative config bridge --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + .github/ISSUE_TEMPLATE/feature_request.yml | 1 + .github/component_owners.yml | 3 + .github/scripts/draft-change-log-entries.sh | 1 + README.md | 55 +++++++------ declarative-config-bridge/README.md | 91 +++++++++++++++++++++ 6 files changed, 125 insertions(+), 27 deletions(-) create mode 100644 declarative-config-bridge/README.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 63d5b270f..1b7d1663f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -13,6 +13,7 @@ body: - aws-xray - aws-xray-propagator - consistent-sampling + - declarative-config-bridge - disk-buffering - gcp-auth-extension - gcp-resources diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 45edd560b..576032700 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -13,6 +13,7 @@ body: - aws-xray - aws-xray-propagator - consistent-sampling + - declarative-config-bridge - disk-buffering - gcp-auth-extension - gcp-resources diff --git a/.github/component_owners.yml b/.github/component_owners.yml index a2a58ed86..b6cde7c96 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -37,6 +37,9 @@ components: consistent-sampling: - oertl - PeterF778 + declarative-config-bridge: + - jaydeluca + - zeitlinger disk-buffering: - LikeTheSalad - zeitlinger diff --git a/.github/scripts/draft-change-log-entries.sh b/.github/scripts/draft-change-log-entries.sh index 7bad595f9..1252889c7 100755 --- a/.github/scripts/draft-change-log-entries.sh +++ b/.github/scripts/draft-change-log-entries.sh @@ -33,6 +33,7 @@ component_names["cel-sampler/"]="Common Expression Language sampler" component_names["cloudfoundry-resources/"]="CloudFoundry resources" component_names["compressors/"]="Compressors" component_names["consistent-sampling/"]="Consistent sampling" +component_names["declarative-config-bridge"]="Declarative config bridge" component_names["disk-buffering/"]="Disk buffering" component_names["gcp-resources/"]="GCP resources" component_names["gcp-auth-extension/"]="GCP authentication extension" diff --git a/README.md b/README.md index c38cdb1ac..048efcc79 100644 --- a/README.md +++ b/README.md @@ -14,34 +14,35 @@ feature or via instrumentation, this project is hopefully for you. ## Provided Libraries -| Status* | Library | -|---------|-------------------------------------------------------------------| -| beta | [AWS Resources](./aws-resources/README.md) | -| stable | [AWS X-Ray SDK Support](./aws-xray/README.md) | -| alpha | [AWS X-Ray Propagator](./aws-xray-propagator/README.md) | -| alpha | [Baggage Processors](./baggage-processor/README.md) | -| alpha | [zstd Compressor](./compressors/compressor-zstd/README.md) | +| Status* | Library | +|---------|--------------------------------------------------------------------| +| beta | [AWS Resources](./aws-resources/README.md) | +| stable | [AWS X-Ray SDK Support](./aws-xray/README.md) | +| alpha | [AWS X-Ray Propagator](./aws-xray-propagator/README.md) | +| alpha | [Baggage Processors](./baggage-processor/README.md) | +| alpha | [zstd Compressor](./compressors/compressor-zstd/README.md) | | alpha | [CEL-Based Sampler](./cel-sampler/README.md) | -| alpha | [Consistent Sampling](./consistent-sampling/README.md) | -| alpha | [Disk Buffering](./disk-buffering/README.md) | -| alpha | [GCP Authentication Extension](./gcp-auth-extension/README.md) | -| beta | [GCP Resources](./gcp-resources/README.md) | -| beta | [Inferred Spans](./inferred-spans/README.md) | -| alpha | [IBM MQ Metrics](./ibm-mq-metrics/README.md) | -| alpha | [JFR Connection](./jfr-connection/README.md) | -| alpha | [JFR Events](./jfr-events/README.md) | -| alpha | [JMX Metric Gatherer](./jmx-metrics/README.md) | -| alpha | [JMX Metric Scraper](./jmx-scraper/README.md) | -| alpha | [Kafka Support](./kafka-exporter/README.md) | -| alpha | [OpenTelemetry Maven Extension](./maven-extension/README.md) | -| alpha | [Micrometer MeterProvider](./micrometer-meter-provider/README.md) | -| alpha | [No-Op API](./noop-api/README.md) | -| alpha | [Intercept and Process Signals Globally](./processors/README.md) | -| alpha | [Prometheus Client Bridge](./prometheus-client-bridge/README.md) | -| alpha | [Resource Providers](./resource-providers/README.md) | -| alpha | [Runtime Attach](./runtime-attach/README.md) | -| alpha | [Samplers](./samplers/README.md) | -| beta | [Span Stacktrace Capture](./span-stacktrace/README.md) | +| alpha | [Consistent Sampling](./consistent-sampling/README.md) | +| alpha | [Declarative Config Bridge](./declarative-config-bridge/README.md) | +| alpha | [Disk Buffering](./disk-buffering/README.md) | +| alpha | [GCP Authentication Extension](./gcp-auth-extension/README.md) | +| beta | [GCP Resources](./gcp-resources/README.md) | +| beta | [Inferred Spans](./inferred-spans/README.md) | +| alpha | [IBM MQ Metrics](./ibm-mq-metrics/README.md) | +| alpha | [JFR Connection](./jfr-connection/README.md) | +| alpha | [JFR Events](./jfr-events/README.md) | +| alpha | [JMX Metric Gatherer](./jmx-metrics/README.md) | +| alpha | [JMX Metric Scraper](./jmx-scraper/README.md) | +| alpha | [Kafka Support](./kafka-exporter/README.md) | +| alpha | [OpenTelemetry Maven Extension](./maven-extension/README.md) | +| alpha | [Micrometer MeterProvider](./micrometer-meter-provider/README.md) | +| alpha | [No-Op API](./noop-api/README.md) | +| alpha | [Intercept and Process Signals Globally](./processors/README.md) | +| alpha | [Prometheus Client Bridge](./prometheus-client-bridge/README.md) | +| alpha | [Resource Providers](./resource-providers/README.md) | +| alpha | [Runtime Attach](./runtime-attach/README.md) | +| alpha | [Samplers](./samplers/README.md) | +| beta | [Span Stacktrace Capture](./span-stacktrace/README.md) | \* `alpha`, `beta` and `stable` are currently used to denote library status per [otep 0232](https://github.com/open-telemetry/oteps/blob/main/text/0232-maturity-of-otel.md). To reach stable status, the library needs to have stable APIs, stable semantic conventions, and be production ready. diff --git a/declarative-config-bridge/README.md b/declarative-config-bridge/README.md new file mode 100644 index 000000000..48cfe0fe6 --- /dev/null +++ b/declarative-config-bridge/README.md @@ -0,0 +1,91 @@ +# Declarative Config Bridge + +Declarative Config Bridge allows instrumentation authors to access configuration in a uniform way, +regardless of the configuration source. + +The bridge allows you to read configuration using the system property style when dealing with +declarative configuration. + +## Example + +As an example, let's look at the inferred spans configuration. +First, there is a configuration method that reads the properties and is unaware of the source of the +configuration: + +```java +public class InferredSpansConfig { + static SpanProcessor create(ConfigProperties properties) { + // read properties here + boolean backupDiagnosticFiles = + properties.getBoolean("otel.inferred.spans.backup.diagnostic.files", false); + } +} +``` + +The auto configuration **without declarative config** passes the provided properties directly: + +```java + +@AutoService(AutoConfigurationCustomizerProvider.class) +public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer config) { + config.addTracerProviderCustomizer( + (providerBuilder, properties) -> { + providerBuilder.addSpanProcessor(InferredSpansConfig.create(properties)); + return providerBuilder; + }); + } +} +``` + +The auto configuration **with declarative config** uses the Declarative Config Bridge to be able to +use common configuration method: + +Let's first look at the yaml file that is used to configure the inferred spans processor: + +```yaml +file_format: 1.0-rc.1 +tracer_provider: + processors: + - inferred_spans: + backup: + diagnostic: + files: true +``` + +And now the component provider that uses the Declarative Config Bridge: + +```java + +@AutoService(ComponentProvider.class) +public class InferredSpansComponentProvider implements ComponentProvider { + + @Override + public String getName() { + return "inferred_spans"; + } + + @Override + public SpanProcessor create(DeclarativeConfigProperties config) { + return InferredSpansAutoConfig.create( + new DeclarativeConfigPropertiesBridgeBuilder() + // crop the prefix, because the properties are under the "inferred_spans" processor + .addMapping("otel.inferred.spans.", "") + .build(config)); + } + + @Override + public Class getType() { + return SpanProcessor.class; + } +} +``` + +## Component owners + +- [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana +- [Jay DeLuca](https://github.com/jaydeluca), Grafana + +Learn more about component owners in [component_owners.yml](../.github/component_owners.yml). From 11f5d806055607f6dccc760d420cddfecc643728 Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:35:42 +0000 Subject: [PATCH 20/36] ./gradlew spotlessApply --- .../contrib/inferredspans/InferredSpansConfig.java | 5 +++++ 1 file changed, 5 insertions(+) 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 index 4a2b26491..e430f01a3 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java @@ -1,3 +1,8 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + package io.opentelemetry.contrib.inferredspans; import io.opentelemetry.api.trace.SpanBuilder; From ba16ddfde0b9c97f43742e0739a0df1804926e8b Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 19 Aug 2025 14:41:31 +0200 Subject: [PATCH 21/36] add description and owner of the declarative config bridge --- declarative-config-bridge/README.md | 4 +-- .../InferredSpansComponentProvider.java | 2 +- .../inferredspans/InferredSpansConfig.java | 5 ++- .../InferredSpansAutoConfigTest.java | 34 +++++++++---------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/declarative-config-bridge/README.md b/declarative-config-bridge/README.md index 48cfe0fe6..8af40618b 100644 --- a/declarative-config-bridge/README.md +++ b/declarative-config-bridge/README.md @@ -13,7 +13,7 @@ First, there is a configuration method that reads the properties and is unaware configuration: ```java -public class InferredSpansConfig { +class InferredSpansConfig { static SpanProcessor create(ConfigProperties properties) { // read properties here boolean backupDiagnosticFiles = @@ -69,7 +69,7 @@ public class InferredSpansComponentProvider implements ComponentProvider processors = OtelReflectionUtils.getSpanProcessors(otel); assertThat(processors).filteredOn(proc -> proc instanceof InferredSpansProcessor).hasSize(1); From 05eeaf8248bf0ce93cdfd07695be133738c725da Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 22 Aug 2025 10:10:25 +0200 Subject: [PATCH 22/36] move bridge to agent --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.yml | 1 - .github/component_owners.yml | 3 - .github/scripts/draft-change-log-entries.sh | 1 - README.md | 55 +++-- declarative-config-bridge/README.md | 91 --------- declarative-config-bridge/build.gradle.kts | 18 -- .../DeclarativeConfigPropertiesBridge.java | 188 ------------------ ...larativeConfigPropertiesBridgeBuilder.java | 104 ---------- ...tiveConfigPropertiesBridgeBuilderTest.java | 90 --------- ...DeclarativeConfigPropertiesBridgeTest.java | 145 -------------- .../src/test/resources/config.yaml | 27 --- inferred-spans/build.gradle.kts | 14 +- .../InferredSpansComponentProvider.java | 2 +- settings.gradle.kts | 1 - 15 files changed, 40 insertions(+), 701 deletions(-) delete mode 100644 declarative-config-bridge/README.md delete mode 100644 declarative-config-bridge/build.gradle.kts delete mode 100644 declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java delete mode 100644 declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java delete mode 100644 declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java delete mode 100644 declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java delete mode 100644 declarative-config-bridge/src/test/resources/config.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 1b7d1663f..63d5b270f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -13,7 +13,6 @@ body: - aws-xray - aws-xray-propagator - consistent-sampling - - declarative-config-bridge - disk-buffering - gcp-auth-extension - gcp-resources diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 576032700..45edd560b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -13,7 +13,6 @@ body: - aws-xray - aws-xray-propagator - consistent-sampling - - declarative-config-bridge - disk-buffering - gcp-auth-extension - gcp-resources diff --git a/.github/component_owners.yml b/.github/component_owners.yml index b6cde7c96..a2a58ed86 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -37,9 +37,6 @@ components: consistent-sampling: - oertl - PeterF778 - declarative-config-bridge: - - jaydeluca - - zeitlinger disk-buffering: - LikeTheSalad - zeitlinger diff --git a/.github/scripts/draft-change-log-entries.sh b/.github/scripts/draft-change-log-entries.sh index 1252889c7..7bad595f9 100755 --- a/.github/scripts/draft-change-log-entries.sh +++ b/.github/scripts/draft-change-log-entries.sh @@ -33,7 +33,6 @@ component_names["cel-sampler/"]="Common Expression Language sampler" component_names["cloudfoundry-resources/"]="CloudFoundry resources" component_names["compressors/"]="Compressors" component_names["consistent-sampling/"]="Consistent sampling" -component_names["declarative-config-bridge"]="Declarative config bridge" component_names["disk-buffering/"]="Disk buffering" component_names["gcp-resources/"]="GCP resources" component_names["gcp-auth-extension/"]="GCP authentication extension" diff --git a/README.md b/README.md index 048efcc79..c38cdb1ac 100644 --- a/README.md +++ b/README.md @@ -14,35 +14,34 @@ feature or via instrumentation, this project is hopefully for you. ## Provided Libraries -| Status* | Library | -|---------|--------------------------------------------------------------------| -| beta | [AWS Resources](./aws-resources/README.md) | -| stable | [AWS X-Ray SDK Support](./aws-xray/README.md) | -| alpha | [AWS X-Ray Propagator](./aws-xray-propagator/README.md) | -| alpha | [Baggage Processors](./baggage-processor/README.md) | -| alpha | [zstd Compressor](./compressors/compressor-zstd/README.md) | +| Status* | Library | +|---------|-------------------------------------------------------------------| +| beta | [AWS Resources](./aws-resources/README.md) | +| stable | [AWS X-Ray SDK Support](./aws-xray/README.md) | +| alpha | [AWS X-Ray Propagator](./aws-xray-propagator/README.md) | +| alpha | [Baggage Processors](./baggage-processor/README.md) | +| alpha | [zstd Compressor](./compressors/compressor-zstd/README.md) | | alpha | [CEL-Based Sampler](./cel-sampler/README.md) | -| alpha | [Consistent Sampling](./consistent-sampling/README.md) | -| alpha | [Declarative Config Bridge](./declarative-config-bridge/README.md) | -| alpha | [Disk Buffering](./disk-buffering/README.md) | -| alpha | [GCP Authentication Extension](./gcp-auth-extension/README.md) | -| beta | [GCP Resources](./gcp-resources/README.md) | -| beta | [Inferred Spans](./inferred-spans/README.md) | -| alpha | [IBM MQ Metrics](./ibm-mq-metrics/README.md) | -| alpha | [JFR Connection](./jfr-connection/README.md) | -| alpha | [JFR Events](./jfr-events/README.md) | -| alpha | [JMX Metric Gatherer](./jmx-metrics/README.md) | -| alpha | [JMX Metric Scraper](./jmx-scraper/README.md) | -| alpha | [Kafka Support](./kafka-exporter/README.md) | -| alpha | [OpenTelemetry Maven Extension](./maven-extension/README.md) | -| alpha | [Micrometer MeterProvider](./micrometer-meter-provider/README.md) | -| alpha | [No-Op API](./noop-api/README.md) | -| alpha | [Intercept and Process Signals Globally](./processors/README.md) | -| alpha | [Prometheus Client Bridge](./prometheus-client-bridge/README.md) | -| alpha | [Resource Providers](./resource-providers/README.md) | -| alpha | [Runtime Attach](./runtime-attach/README.md) | -| alpha | [Samplers](./samplers/README.md) | -| beta | [Span Stacktrace Capture](./span-stacktrace/README.md) | +| alpha | [Consistent Sampling](./consistent-sampling/README.md) | +| alpha | [Disk Buffering](./disk-buffering/README.md) | +| alpha | [GCP Authentication Extension](./gcp-auth-extension/README.md) | +| beta | [GCP Resources](./gcp-resources/README.md) | +| beta | [Inferred Spans](./inferred-spans/README.md) | +| alpha | [IBM MQ Metrics](./ibm-mq-metrics/README.md) | +| alpha | [JFR Connection](./jfr-connection/README.md) | +| alpha | [JFR Events](./jfr-events/README.md) | +| alpha | [JMX Metric Gatherer](./jmx-metrics/README.md) | +| alpha | [JMX Metric Scraper](./jmx-scraper/README.md) | +| alpha | [Kafka Support](./kafka-exporter/README.md) | +| alpha | [OpenTelemetry Maven Extension](./maven-extension/README.md) | +| alpha | [Micrometer MeterProvider](./micrometer-meter-provider/README.md) | +| alpha | [No-Op API](./noop-api/README.md) | +| alpha | [Intercept and Process Signals Globally](./processors/README.md) | +| alpha | [Prometheus Client Bridge](./prometheus-client-bridge/README.md) | +| alpha | [Resource Providers](./resource-providers/README.md) | +| alpha | [Runtime Attach](./runtime-attach/README.md) | +| alpha | [Samplers](./samplers/README.md) | +| beta | [Span Stacktrace Capture](./span-stacktrace/README.md) | \* `alpha`, `beta` and `stable` are currently used to denote library status per [otep 0232](https://github.com/open-telemetry/oteps/blob/main/text/0232-maturity-of-otel.md). To reach stable status, the library needs to have stable APIs, stable semantic conventions, and be production ready. diff --git a/declarative-config-bridge/README.md b/declarative-config-bridge/README.md deleted file mode 100644 index 8af40618b..000000000 --- a/declarative-config-bridge/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Declarative Config Bridge - -Declarative Config Bridge allows instrumentation authors to access configuration in a uniform way, -regardless of the configuration source. - -The bridge allows you to read configuration using the system property style when dealing with -declarative configuration. - -## Example - -As an example, let's look at the inferred spans configuration. -First, there is a configuration method that reads the properties and is unaware of the source of the -configuration: - -```java -class InferredSpansConfig { - static SpanProcessor create(ConfigProperties properties) { - // read properties here - boolean backupDiagnosticFiles = - properties.getBoolean("otel.inferred.spans.backup.diagnostic.files", false); - } -} -``` - -The auto configuration **without declarative config** passes the provided properties directly: - -```java - -@AutoService(AutoConfigurationCustomizerProvider.class) -public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvider { - - @Override - public void customize(AutoConfigurationCustomizer config) { - config.addTracerProviderCustomizer( - (providerBuilder, properties) -> { - providerBuilder.addSpanProcessor(InferredSpansConfig.create(properties)); - return providerBuilder; - }); - } -} -``` - -The auto configuration **with declarative config** uses the Declarative Config Bridge to be able to -use common configuration method: - -Let's first look at the yaml file that is used to configure the inferred spans processor: - -```yaml -file_format: 1.0-rc.1 -tracer_provider: - processors: - - inferred_spans: - backup: - diagnostic: - files: true -``` - -And now the component provider that uses the Declarative Config Bridge: - -```java - -@AutoService(ComponentProvider.class) -public class InferredSpansComponentProvider implements ComponentProvider { - - @Override - public String getName() { - return "inferred_spans"; - } - - @Override - public SpanProcessor create(DeclarativeConfigProperties config) { - return InferredSpansConfig.create( - new DeclarativeConfigPropertiesBridgeBuilder() - // crop the prefix, because the properties are under the "inferred_spans" processor - .addMapping("otel.inferred.spans.", "") - .build(config)); - } - - @Override - public Class getType() { - return SpanProcessor.class; - } -} -``` - -## Component owners - -- [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana -- [Jay DeLuca](https://github.com/jaydeluca), Grafana - -Learn more about component owners in [component_owners.yml](../.github/component_owners.yml). diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts deleted file mode 100644 index 8e20094b4..000000000 --- a/declarative-config-bridge/build.gradle.kts +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id("otel.java-conventions") - id("otel.publish-conventions") -} - -description = "OpenTelemetry extension that provides a bridge for declarative configuration." -otelJava.moduleName.set("io.opentelemetry.contrib.sdk.config.bridge") - -dependencies { - // We use `compileOnly` dependency because during runtime all necessary classes are provided by - // javaagent itself. - compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") - compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") - - testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") - testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") - testImplementation("org.mockito:mockito-inline") -} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java deleted file mode 100644 index c5384097c..000000000 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.contrib.sdk.config.bridge; - -import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; - -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiFunction; -import javax.annotation.Nullable; - -/** - * A {@link ConfigProperties} which resolves properties based on {@link - * DeclarativeConfigProperties}. - * - *

Only properties starting with "otel.instrumentation." are resolved. Others return null (or - * default value if provided). - * - *

To resolve: - * - *

    - *
  • "otel.instrumentation" refers to the ".instrumentation.java" node - *
  • The portion of the property after "otel.instrumentation." is split into segments based on - * ".". - *
  • For each N-1 segment, we walk down the tree to find the relevant leaf {@link - * DeclarativeConfigProperties}. - *
  • We extract the property from the resolved {@link DeclarativeConfigProperties} using the - * last segment as the property key. - *
- * - *

For example, given the following YAML, asking for {@code - * ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value": - * - *

- *   instrumentation:
- *     java:
- *       common:
- *         string_key: value
- * 
- */ -final class DeclarativeConfigPropertiesBridge implements ConfigProperties { - - private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation."; - - private final DeclarativeConfigProperties baseNode; - - // lookup order matters - we choose the first match - private final Map mappings; - private final Map overrideValues; - - DeclarativeConfigPropertiesBridge( - DeclarativeConfigProperties baseNode, - Map mappings, - Map overrideValues) { - this.baseNode = Objects.requireNonNull(baseNode); - this.mappings = mappings; - this.overrideValues = overrideValues; - } - - @Nullable - @Override - public String getString(String propertyName) { - return getPropertyValue(propertyName, String.class, DeclarativeConfigProperties::getString); - } - - @Nullable - @Override - public Boolean getBoolean(String propertyName) { - return getPropertyValue(propertyName, Boolean.class, DeclarativeConfigProperties::getBoolean); - } - - @Nullable - @Override - public Integer getInt(String propertyName) { - return getPropertyValue(propertyName, Integer.class, DeclarativeConfigProperties::getInt); - } - - @Nullable - @Override - public Long getLong(String propertyName) { - return getPropertyValue(propertyName, Long.class, DeclarativeConfigProperties::getLong); - } - - @Nullable - @Override - public Double getDouble(String propertyName) { - return getPropertyValue(propertyName, Double.class, DeclarativeConfigProperties::getDouble); - } - - @Nullable - @Override - public Duration getDuration(String propertyName) { - Long millis = getPropertyValue(propertyName, Long.class, DeclarativeConfigProperties::getLong); - if (millis == null) { - return null; - } - return Duration.ofMillis(millis); - } - - @SuppressWarnings("unchecked") - @Override - public List getList(String propertyName) { - List propertyValue = - getPropertyValue( - propertyName, - List.class, - (properties, lastPart) -> properties.getScalarList(lastPart, String.class)); - return propertyValue == null ? Collections.emptyList() : propertyValue; - } - - @SuppressWarnings("unchecked") - @Override - public Map getMap(String propertyName) { - DeclarativeConfigProperties propertyValue = - getPropertyValue( - propertyName, - DeclarativeConfigProperties.class, - DeclarativeConfigProperties::getStructured); - if (propertyValue == null) { - return Collections.emptyMap(); - } - Map result = new HashMap<>(); - propertyValue - .getPropertyKeys() - .forEach( - key -> { - String value = propertyValue.getString(key); - if (value == null) { - return; - } - result.put(key, value); - }); - return Collections.unmodifiableMap(result); - } - - @Nullable - private T getPropertyValue( - String property, - Class clazz, - BiFunction extractor) { - T override = clazz.cast(overrideValues.get(property)); - if (override != null) { - return override; - } - - String[] segments = getSegments(translateProperty(property)); - if (segments.length == 0) { - return null; - } - - // Extract the value by walking to the N-1 entry - DeclarativeConfigProperties target = baseNode; - if (segments.length > 1) { - for (int i = 0; i < segments.length - 1; i++) { - target = target.getStructured(segments[i], empty()); - } - } - String lastPart = segments[segments.length - 1]; - - return extractor.apply(target, lastPart); - } - - static String[] getSegments(String property) { - if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { - property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); - } - // Split the remainder of the property on "." - return property.replace('-', '_').split("\\."); - } - - private String translateProperty(String property) { - for (Map.Entry entry : mappings.entrySet()) { - if (property.startsWith(entry.getKey())) { - return entry.getValue() + property.substring(entry.getKey().length()); - } - } - return property; - } -} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java b/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java deleted file mode 100644 index 7f00bdd5f..000000000 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/contrib/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.contrib.sdk.config.bridge; - -import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; - -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import io.opentelemetry.api.incubator.config.ConfigProvider; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import javax.annotation.Nullable; - -/** - * A builder for {@link DeclarativeConfigPropertiesBridge} that allows adding translations and fixed - * values for properties. - */ -public class DeclarativeConfigPropertiesBridgeBuilder { - /** - * order is important here, so we use LinkedHashMap - see {@link #addMapping(String, String)} for - * more details - */ - private final Map mappings = new LinkedHashMap<>(); - - private final Map overrideValues = new HashMap<>(); - - public DeclarativeConfigPropertiesBridgeBuilder() {} - - /** - * Adds a mapping from a property prefix to a YAML path. - * - *

For example, if the property prefix is "otel.javaagent" and the YAML path is "agent", then - * any property starting with "otel.javaagent." will be resolved against the "agent" node in the - * instrumentation/java section of the YAML configuration. - * - * @param propertyPrefix the prefix of the property to translate - * @param yamlPath the YAML path to resolve the property against - */ - @CanIgnoreReturnValue - public DeclarativeConfigPropertiesBridgeBuilder addMapping( - String propertyPrefix, String yamlPath) { - mappings.put(propertyPrefix, yamlPath); - return this; - } - - /** - * Adds a fixed override value for a property. - * - * @param propertyName the name of the property to override - * @param value the value to return when the property is requested - */ - @CanIgnoreReturnValue - public DeclarativeConfigPropertiesBridgeBuilder addOverride(String propertyName, Object value) { - overrideValues.put(propertyName, value); - return this; - } - - /** Build {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */ - public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { - ConfigProperties sdkConfigProperties = - AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); - if (sdkConfigProperties != null) { - return sdkConfigProperties; - } - ConfigProvider configProvider = - AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk); - if (configProvider != null) { - return buildFromInstrumentationConfig(configProvider.getInstrumentationConfig()); - } - // Should never happen - throw new IllegalStateException( - "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); - } - - /** - * Build {@link DeclarativeConfigPropertiesBridge} from the {@link DeclarativeConfigProperties}. - * - * @param node the declarative config properties to build from - * @return a new instance of {@link ConfigProperties} - */ - public ConfigProperties build(@Nullable DeclarativeConfigProperties node) { - return new DeclarativeConfigPropertiesBridge( - node == null ? empty() : node, mappings, overrideValues); - } - - /** - * Build {@link ConfigProperties} from the {@link DeclarativeConfigProperties}. - * - * @param instrumentationConfig the instrumentation configuration to build from - * @return a new instance of {@link ConfigProperties} - */ - public ConfigProperties buildFromInstrumentationConfig( - @Nullable DeclarativeConfigProperties instrumentationConfig) { - return build( - instrumentationConfig == null ? null : instrumentationConfig.getStructured("java")); - } -} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java deleted file mode 100644 index a353a307f..000000000 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeBuilderTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.contrib.sdk.autoconfigure; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.opentelemetry.api.incubator.config.ConfigProvider; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.contrib.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; -import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -@SuppressWarnings("DoNotMockAutoValue") -class DeclarativeConfigPropertiesBridgeBuilderTest { - @Test - void shouldUseConfigPropertiesForAutoConfiguration() { - ConfigProperties configPropertiesMock = mock(ConfigProperties.class); - AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); - try (MockedStatic autoConfigureUtilMock = - Mockito.mockStatic(AutoConfigureUtil.class)) { - autoConfigureUtilMock - .when(() -> AutoConfigureUtil.getConfig(sdkMock)) - .thenReturn(configPropertiesMock); - - ConfigProperties configProperties = - new DeclarativeConfigPropertiesBridgeBuilder().build(sdkMock); - - assertThat(configProperties).isSameAs(configPropertiesMock); - } - } - - @Test - void shouldUseConfigProviderForDeclarativeConfiguration() { - String propertyName = "testProperty"; - String expectedValue = "the value"; - DeclarativeConfigProperties javaNodeMock = mock(DeclarativeConfigProperties.class); - when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue); - - DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class); - when(instrumentationConfigMock.getStructured(eq("java"))).thenReturn(javaNodeMock); - - ConfigProvider configProviderMock = mock(ConfigProvider.class); - when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock); - - AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); - - try (MockedStatic autoConfigureUtilMock = - Mockito.mockStatic(AutoConfigureUtil.class)) { - autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null); - autoConfigureUtilMock - .when(() -> AutoConfigureUtil.getConfigProvider(sdkMock)) - .thenReturn(configProviderMock); - - ConfigProperties configProperties = - new DeclarativeConfigPropertiesBridgeBuilder().build(sdkMock); - - assertThat(configProperties.getString(propertyName)).isEqualTo(expectedValue); - } - } - - @Test - void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig() { - AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class); - ConfigProvider configProviderMock = mock(ConfigProvider.class); - when(configProviderMock.getInstrumentationConfig()).thenReturn(null); - - try (MockedStatic autoConfigureUtilMock = - Mockito.mockStatic(AutoConfigureUtil.class)) { - autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null); - autoConfigureUtilMock - .when(() -> AutoConfigureUtil.getConfigProvider(sdkMock)) - .thenReturn(configProviderMock); - - ConfigProperties configProperties = - new DeclarativeConfigPropertiesBridgeBuilder().build(sdkMock); - - assertThat(configProperties.getString("testProperty")).isNull(); - } - } -} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java deleted file mode 100644 index ac08e221b..000000000 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/contrib/sdk/autoconfigure/DeclarativeConfigPropertiesBridgeTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.contrib.sdk.autoconfigure; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.opentelemetry.contrib.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; -import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.InstrumentationModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import java.time.Duration; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class DeclarativeConfigPropertiesBridgeTest { - - private ConfigProperties bridge; - private ConfigProperties emptyBridge; - - @BeforeEach - void setup() { - bridge = create(new DeclarativeConfigPropertiesBridgeBuilder()); - - OpenTelemetryConfigurationModel emptyModel = - new OpenTelemetryConfigurationModel() - .withAdditionalProperty("instrumentation/development", new InstrumentationModel()); - SdkConfigProvider emptyConfigProvider = SdkConfigProvider.create(emptyModel); - emptyBridge = - new DeclarativeConfigPropertiesBridgeBuilder() - .buildFromInstrumentationConfig( - Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig())); - } - - private static ConfigProperties create(DeclarativeConfigPropertiesBridgeBuilder builder) { - OpenTelemetryConfigurationModel model = - DeclarativeConfiguration.parse( - DeclarativeConfigPropertiesBridgeTest.class - .getClassLoader() - .getResourceAsStream("config.yaml")); - return builder.buildFromInstrumentationConfig( - SdkConfigProvider.create(model).getInstrumentationConfig()); - } - - @Test - void getProperties() { - // only properties starting with "otel.instrumentation." are resolved - // asking for properties which don't exist or inaccessible shouldn't result in an error - assertThat(bridge.getString("file_format")).isNull(); - assertThat(bridge.getString("file_format", "foo")).isEqualTo("foo"); - assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled")).isNull(); - assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled", true)) - .isTrue(); - - // common cases - assertThat(bridge.getBoolean("otel.instrumentation.runtime-telemetry.enabled")).isFalse(); - - // check all the types - Map expectedMap = new HashMap<>(); - expectedMap.put("string_key1", "value1"); - expectedMap.put("string_key2", "value2"); - assertThat(bridge.getString("otel.instrumentation.example-instrumentation.string_key")) - .isEqualTo("value"); - assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.bool_key")).isTrue(); - assertThat(bridge.getInt("otel.instrumentation.example-instrumentation.int_key")).isEqualTo(1); - assertThat(bridge.getLong("otel.instrumentation.example-instrumentation.int_key")) - .isEqualTo(1L); - assertThat(bridge.getDuration("otel.instrumentation.example-instrumentation.int_key")) - .isEqualTo(Duration.ofMillis(1)); - assertThat(bridge.getDouble("otel.instrumentation.example-instrumentation.double_key")) - .isEqualTo(1.1); - assertThat(bridge.getList("otel.instrumentation.example-instrumentation.list_key")) - .isEqualTo(Arrays.asList("value1", "value2")); - assertThat(bridge.getMap("otel.instrumentation.example-instrumentation.map_key")) - .isEqualTo(expectedMap); - - // asking for properties with the wrong type returns null - assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.string_key")) - .isNull(); - assertThat(bridge.getString("otel.instrumentation.example-instrumentation.bool_key")).isNull(); - assertThat(bridge.getString("otel.instrumentation.example-instrumentation.int_key")).isNull(); - assertThat(bridge.getString("otel.instrumentation.example-instrumentation.double_key")) - .isNull(); - assertThat(bridge.getString("otel.instrumentation.example-instrumentation.list_key")).isNull(); - assertThat(bridge.getString("otel.instrumentation.example-instrumentation.map_key")).isNull(); - - // check all the types - assertThat(bridge.getString("otel.instrumentation.other-instrumentation.string_key", "value")) - .isEqualTo("value"); - assertThat(bridge.getBoolean("otel.instrumentation.other-instrumentation.bool_key", true)) - .isTrue(); - assertThat(bridge.getInt("otel.instrumentation.other-instrumentation.int_key", 1)).isEqualTo(1); - assertThat(bridge.getLong("otel.instrumentation.other-instrumentation.int_key", 1L)) - .isEqualTo(1L); - assertThat( - bridge.getDuration( - "otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1))) - .isEqualTo(Duration.ofMillis(1)); - assertThat(bridge.getDouble("otel.instrumentation.other-instrumentation.double_key", 1.1)) - .isEqualTo(1.1); - assertThat( - bridge.getList( - "otel.instrumentation.other-instrumentation.list_key", - Arrays.asList("value1", "value2"))) - .isEqualTo(Arrays.asList("value1", "value2")); - assertThat(bridge.getMap("otel.instrumentation.other-instrumentation.map_key", expectedMap)) - .isEqualTo(expectedMap); - } - - @Test - void vendor() { - // verify vendor specific property names are preserved in unchanged form (prefix is not stripped - // as for otel.instrumentation.*) - assertThat(bridge.getBoolean("acme.full_name.preserved")).isTrue(); - } - - @Test - void vendorTranslation() { - ConfigProperties propertiesBridge = - create(new DeclarativeConfigPropertiesBridgeBuilder().addMapping("acme", "acme.full_name")); - assertThat(propertiesBridge.getBoolean("acme.preserved")).isTrue(); - } - - @Test - void agentTranslation() { - ConfigProperties bridge = - create( - new DeclarativeConfigPropertiesBridgeBuilder() - .addMapping("otel.javaagent", "agent") - .addOverride("otel.javaagent.debug", true) - .addOverride("otel.javaagent.logging", "application")); - - assertThat(bridge.getBoolean("otel.javaagent.debug")).isTrue(); - assertThat(bridge.getBoolean("otel.javaagent.experimental.indy")).isTrue(); - assertThat(bridge.getString("otel.javaagent.logging")).isEqualTo("application"); - } -} diff --git a/declarative-config-bridge/src/test/resources/config.yaml b/declarative-config-bridge/src/test/resources/config.yaml deleted file mode 100644 index 6c8d7fddc..000000000 --- a/declarative-config-bridge/src/test/resources/config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -file_format: 1.0-rc.1 -instrumentation/development: - java: - acme: - full_name: - preserved: true - agent: - experimental: - indy: true - common: - default: - enabled: false - runtime_telemetry: - enabled: false - example_instrumentation: - string_key: value - bool_key: true - int_key: 1 - double_key: 1.1 - list_key: - - value1 - - value2 - - true - map_key: - string_key1: value1 - string_key2: value2 - bool_key: true diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts index bdf94a906..c1c5eac6c 100644 --- a/inferred-spans/build.gradle.kts +++ b/inferred-spans/build.gradle.kts @@ -9,13 +9,12 @@ description = "OpenTelemetry Java profiling based inferred spans module" otelJava.moduleName.set("io.opentelemetry.contrib.inferredspans") dependencies { - implementation(project(":declarative-config-bridge")) - 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") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") + compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator") compileOnly("io.opentelemetry.semconv:opentelemetry-semconv") implementation("com.lmax:disruptor") implementation("org.jctools:jctools-core") @@ -32,6 +31,7 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry:opentelemetry-api-incubator") testImplementation("io.opentelemetry:opentelemetry-exporter-logging") + testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator") } tasks { @@ -52,3 +52,13 @@ tasks { jvmArgs("-Djava.util.logging.config.file=${project.projectDir.resolve("src/test/resources/logging.properties")}") } } + +// todo remove when https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14497 is merged +// pin io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator to 2.20.0-alpha-SNAPSHOT + +configurations.all { + resolutionStrategy { + force("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.20.0-alpha-SNAPSHOT") + force("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.19.0") + } +} diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 287b067e2..1fcbaa73c 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -7,7 +7,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.contrib.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; +import io.opentelemetry.instrumentation.api.incubator.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.trace.SpanProcessor; diff --git a/settings.gradle.kts b/settings.gradle.kts index f812b25c5..01ea45838 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -80,7 +80,6 @@ include(":cel-sampler") include(":compressors:compressor-zstd") include(":cloudfoundry-resources") include(":consistent-sampling") -include(":declarative-config-bridge") include(":dependencyManagement") include(":disk-buffering") include(":ibm-mq-metrics") From c66298b709738ef9d48b1a9b2a80d8a5239a3b87 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 12 Sep 2025 14:09:03 +0200 Subject: [PATCH 23/36] rebase --- .../contrib/inferredspans/InferredSpansConfig.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 index 73f9738e3..41a173bc5 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java @@ -5,6 +5,8 @@ package io.opentelemetry.contrib.inferredspans; +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; @@ -14,7 +16,6 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.stream.Collectors; import javax.annotation.Nullable; class InferredSpansConfig { @@ -103,7 +104,7 @@ void applyWildcards(String configKey, Consumer> fu Arrays.stream(wildcardListString.split(",")) .filter(str -> !str.isEmpty()) .map(WildcardMatcher::valueOf) - .collect(Collectors.toList()); + .collect(toList()); if (!values.isEmpty()) { funcToApply.accept(values); } From 4ddb381a37428f5ec6a5ab8ab84e1269e522aad9 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 15 Sep 2025 10:54:15 +0200 Subject: [PATCH 24/36] update --- inferred-spans/build.gradle.kts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts index c1c5eac6c..dd1304bb8 100644 --- a/inferred-spans/build.gradle.kts +++ b/inferred-spans/build.gradle.kts @@ -52,13 +52,3 @@ tasks { jvmArgs("-Djava.util.logging.config.file=${project.projectDir.resolve("src/test/resources/logging.properties")}") } } - -// todo remove when https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14497 is merged -// pin io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator to 2.20.0-alpha-SNAPSHOT - -configurations.all { - resolutionStrategy { - force("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.20.0-alpha-SNAPSHOT") - force("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.19.0") - } -} From 559f501c1abd20c75c56c9a4e90706c14553212f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 15 Sep 2025 11:04:58 +0200 Subject: [PATCH 25/36] update --- inferred-spans/build.gradle.kts | 4 ++-- .../contrib/inferredspans/InferredSpansComponentProvider.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/inferred-spans/build.gradle.kts b/inferred-spans/build.gradle.kts index dd1304bb8..01522095c 100644 --- a/inferred-spans/build.gradle.kts +++ b/inferred-spans/build.gradle.kts @@ -14,7 +14,7 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-sdk") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") - compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator") + compileOnly("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge") compileOnly("io.opentelemetry.semconv:opentelemetry-semconv") implementation("com.lmax:disruptor") implementation("org.jctools:jctools-core") @@ -31,7 +31,7 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry:opentelemetry-api-incubator") testImplementation("io.opentelemetry:opentelemetry-exporter-logging") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator") + testImplementation("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge") } tasks { diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 1fcbaa73c..4ebd29a2e 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -7,7 +7,7 @@ import com.google.auto.service.AutoService; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.instrumentation.api.incubator.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; +import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.trace.SpanProcessor; From eca9228093398b978e5a99dd30dbcb9ce7a02a25 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 15 Sep 2025 11:32:45 +0200 Subject: [PATCH 26/36] readme --- inferred-spans/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/inferred-spans/README.md b/inferred-spans/README.md index 667f0d201..27a9c2eeb 100644 --- a/inferred-spans/README.md +++ b/inferred-spans/README.md @@ -42,6 +42,32 @@ So if you are using an autoconfigured OpenTelemetry SDK, you'll only need to add | otel.inferred.spans.duration
OTEL_INFERRED_SPANS_DURATION | `5s` | The duration of a profiling session. For sampled transactions which fall within a profiling session (they start after and end before the session), so-called inferred spans will be created. They appear in the trace waterfall view like regular spans.
NOTE: It is not recommended to set much higher durations as it may fill the activation events file and async-profiler's frame buffer. Warnings will be logged if the activation events file is full. If you want to have more profiling coverage, try decreasing `profiling_inferred_spans_interval` | | 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: 0.4 +tracer_provider: + processors: + - inferred-spans: + enabled: true + 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 From 6b2f57ec750e1d2819766c9a5a8245205a819ce0 Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:34:37 +0000 Subject: [PATCH 27/36] ./gradlew spotlessApply --- inferred-spans/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inferred-spans/README.md b/inferred-spans/README.md index 27a9c2eeb..d4ecdfeb7 100644 --- a/inferred-spans/README.md +++ b/inferred-spans/README.md @@ -42,11 +42,11 @@ So if you are using an autoconfigured OpenTelemetry SDK, you'll only need to add | otel.inferred.spans.duration
OTEL_INFERRED_SPANS_DURATION | `5s` | The duration of a profiling session. For sampled transactions which fall within a profiling session (they start after and end before the session), so-called inferred spans will be created. They appear in the trace waterfall view like regular spans.
NOTE: It is not recommended to set much higher durations as it may fill the activation events file and async-profiler's frame buffer. Warnings will be logged if the activation events file is full. If you want to have more profiling coverage, try decreasing `profiling_inferred_spans_interval` | | 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 +You can configure the inferred spans processor using declarative YAML configuration with the OpenTelemetry SDK. For example: ```yaml @@ -65,8 +65,8 @@ tracer_provider: 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. +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 From 0baba1e5c057412e0238f199fd8f1d2a77fbde44 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 15 Sep 2025 18:11:40 +0200 Subject: [PATCH 28/36] lint --- inferred-spans/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/inferred-spans/README.md b/inferred-spans/README.md index d4ecdfeb7..e4ca95a0b 100644 --- a/inferred-spans/README.md +++ b/inferred-spans/README.md @@ -43,7 +43,6 @@ 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 From 69a9ba0bc03f499f4fbca8a1af565aa3502e6d44 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 17 Sep 2025 10:28:51 +0200 Subject: [PATCH 29/36] fix processor name --- inferred-spans/README.md | 4 ++-- .../contrib/inferredspans/InferredSpansComponentProvider.java | 4 ++-- .../inferredspans/InferredSpansCustomizerProviderTest.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/inferred-spans/README.md b/inferred-spans/README.md index e4ca95a0b..2ed1e28f4 100644 --- a/inferred-spans/README.md +++ b/inferred-spans/README.md @@ -49,10 +49,10 @@ You can configure the inferred spans processor using declarative YAML configurat OpenTelemetry SDK. For example: ```yaml -file_format: 0.4 +file_format: 1.0-rc.1 tracer_provider: processors: - - inferred-spans: + - experimental_inferred_spans: enabled: true sampling_interval: 25ms included_classes: "org.example.myapp.*" diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 4ebd29a2e..2031772bf 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -17,14 +17,14 @@ public class InferredSpansComponentProvider implements ComponentProvider Date: Wed, 17 Sep 2025 08:31:06 +0000 Subject: [PATCH 30/36] ./gradlew spotlessApply --- .../contrib/inferredspans/InferredSpansComponentProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java index 2031772bf..1d12978f3 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java @@ -24,7 +24,8 @@ public String getName() { public SpanProcessor create(DeclarativeConfigProperties config) { return InferredSpansConfig.create( new DeclarativeConfigPropertiesBridgeBuilder() - // crop the prefix, because the properties are under the "experimental_inferred_spans" processor + // crop the prefix, because the properties are under the "experimental_inferred_spans" + // processor .addMapping("otel.inferred.spans.", "") .build(config)); } From 775e3114c98f1bcc7e692250e3df282c8a872e0f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 30 Oct 2025 16:59:15 +0100 Subject: [PATCH 31/36] pr review --- .../InferredSpansAutoConfig.java | 2 +- .../inferredspans/InferredSpansConfig.java | 20 ++++++++++++++++++- ...> InferredSpansSpanProcessorProvider.java} | 20 ++++++++++++------- .../InferredSpansCustomizerProviderTest.java | 4 +--- 4 files changed, 34 insertions(+), 12 deletions(-) rename inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/{InferredSpansComponentProvider.java => InferredSpansSpanProcessorProvider.java} (54%) 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 ff71dfba5..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 @@ -22,7 +22,7 @@ public void customize(AutoConfigurationCustomizer config) { config.addTracerProviderCustomizer( (providerBuilder, properties) -> { if (properties.getBoolean(ENABLED_OPTION, false)) { - providerBuilder.addSpanProcessor(InferredSpansConfig.create(properties)); + providerBuilder.addSpanProcessor(InferredSpansConfig.createSpanProcessor(properties)); } else { log.finest( "Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set"); 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 index 41a173bc5..68542240f 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java @@ -5,6 +5,7 @@ package io.opentelemetry.contrib.inferredspans; +import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; import io.opentelemetry.api.trace.SpanBuilder; @@ -37,7 +38,24 @@ private InferredSpansConfig() {} static final String PARENT_OVERRIDE_HANDLER_OPTION = "otel.inferred.spans.parent.override.handler"; - static SpanProcessor create(ConfigProperties properties) { + 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(); PropertiesApplier applier = new PropertiesApplier(properties); diff --git a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java similarity index 54% rename from inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java rename to inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java index 1d12978f3..bb8d04b74 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansComponentProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java @@ -13,7 +13,9 @@ @SuppressWarnings("rawtypes") @AutoService(ComponentProvider.class) -public class InferredSpansComponentProvider implements ComponentProvider { +public class InferredSpansSpanProcessorProvider implements ComponentProvider { + + private static final String PREFIX = "otel.inferred.spans."; @Override public String getName() { @@ -22,12 +24,16 @@ public String getName() { @Override public SpanProcessor create(DeclarativeConfigProperties config) { - return InferredSpansConfig.create( - new DeclarativeConfigPropertiesBridgeBuilder() - // crop the prefix, because the properties are under the "experimental_inferred_spans" - // processor - .addMapping("otel.inferred.spans.", "") - .build(config)); + DeclarativeConfigPropertiesBridgeBuilder builder = + new DeclarativeConfigPropertiesBridgeBuilder(); + + for (String property : InferredSpansConfig.ALL_PROPERTIES) { + // 1. crop the prefix, because the properties are under the "experimental_inferred_spans" + // 2. we want all properties flat under "otel.inferred.spans.*" + builder.addMapping(property, property.substring(PREFIX.length()).replace('.', '_')); + } + + return InferredSpansConfig.createSpanProcessor(builder.build(config)); } @Override diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java index 04e82ecc3..0fdfe3df7 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java @@ -37,9 +37,7 @@ void declarativeConfig() { + " processors:\n" + " - experimental_inferred_spans:\n" + " enabled: false\n" - + " backup:\n" - + " diagnostic:\n" - + " files: true\n"; + + " backup_diagnostic_files: true\n"; OpenTelemetrySdk sdk = DeclarativeConfiguration.parseAndCreate( From 614903c3f3d04cb37f7798d7862b97e018e7c0ba Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 31 Oct 2025 12:41:09 +0100 Subject: [PATCH 32/36] add enabled --- inferred-spans/README.md | 2 +- .../InferredSpansSpanProcessorProvider.java | 15 +++++++++-- ...ferredSpansSpanProcessorProviderTest.java} | 27 +++++++++++++++++-- 3 files changed, 39 insertions(+), 5 deletions(-) rename inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/{InferredSpansCustomizerProviderTest.java => InferredSpansSpanProcessorProviderTest.java} (68%) diff --git a/inferred-spans/README.md b/inferred-spans/README.md index 2ed1e28f4..2d26e601b 100644 --- a/inferred-spans/README.md +++ b/inferred-spans/README.md @@ -53,7 +53,7 @@ file_format: 1.0-rc.1 tracer_provider: processors: - experimental_inferred_spans: - enabled: true + enabled: true # true by default unlike autoconfiguration described above sampling_interval: 25ms included_classes: "org.example.myapp.*" excluded_classes: "java.*" 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 index bb8d04b74..b875c7a77 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java @@ -8,13 +8,18 @@ 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 @@ -23,7 +28,7 @@ public String getName() { } @Override - public SpanProcessor create(DeclarativeConfigProperties config) { + public SpanProcessor create(DeclarativeConfigProperties declarativeConfigProperties) { DeclarativeConfigPropertiesBridgeBuilder builder = new DeclarativeConfigPropertiesBridgeBuilder(); @@ -33,7 +38,13 @@ public SpanProcessor create(DeclarativeConfigProperties config) { builder.addMapping(property, property.substring(PREFIX.length()).replace('.', '_')); } - return InferredSpansConfig.createSpanProcessor(builder.build(config)); + 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 diff --git a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java similarity index 68% rename from inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java rename to inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java index 0fdfe3df7..7cc350cb6 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansCustomizerProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.condition.DisabledOnOs; @DisabledOnOs(WINDOWS) // Uses async-profiler, which is not supported on Windows -class InferredSpansCustomizerProviderTest { +class InferredSpansSpanProcessorProviderTest { @BeforeEach @AfterEach @@ -36,7 +36,6 @@ void declarativeConfig() { + "tracer_provider:\n" + " processors:\n" + " - experimental_inferred_spans:\n" - + " enabled: false\n" + " backup_diagnostic_files: true\n"; OpenTelemetrySdk sdk = @@ -53,4 +52,28 @@ void declarativeConfig() { .extracting("backupDiagnosticFiles") .isEqualTo(true); } + + @Test + void declarativeConfigDisabled() { + String yaml = + "file_format: 1.0-rc.1\n" + + "tracer_provider:\n" + + " processors:\n" + + " - experimental_inferred_spans:\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")); + } } From b527dfeead6bd5913e87298367ce236b786c9294 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 31 Oct 2025 13:54:07 +0100 Subject: [PATCH 33/36] add enabled --- .../contrib/inferredspans/InferredSpansConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 index 68542240f..9b8ff2ca7 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansConfig.java @@ -56,11 +56,10 @@ private InferredSpansConfig() {} PARENT_OVERRIDE_HANDLER_OPTION)); static SpanProcessor createSpanProcessor(ConfigProperties properties) { - InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder(); + InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder().profilerEnabled(true); PropertiesApplier applier = new PropertiesApplier(properties); - applier.applyBool(ENABLED_OPTION, builder::profilerEnabled); applier.applyBool(LOGGING_OPTION, builder::profilerLoggingEnabled); applier.applyBool(DIAGNOSTIC_FILES_OPTION, builder::backupDiagnosticFiles); applier.applyInt(SAFEMODE_OPTION, builder::asyncProfilerSafeMode); From d8faf6eb39dc2e418fd1a7331100aa5a88dd6e29 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 3 Nov 2025 11:13:24 +0100 Subject: [PATCH 34/36] fix processor name --- inferred-spans/README.md | 2 +- .../inferredspans/InferredSpansSpanProcessorProvider.java | 4 ++-- .../inferredspans/InferredSpansSpanProcessorProviderTest.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/inferred-spans/README.md b/inferred-spans/README.md index 2d26e601b..f7486ad75 100644 --- a/inferred-spans/README.md +++ b/inferred-spans/README.md @@ -52,7 +52,7 @@ OpenTelemetry SDK. For example: file_format: 1.0-rc.1 tracer_provider: processors: - - experimental_inferred_spans: + - inferred_spans/development: enabled: true # true by default unlike autoconfiguration described above sampling_interval: 25ms included_classes: "org.example.myapp.*" 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 index b875c7a77..19a1afd90 100644 --- a/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java +++ b/inferred-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProvider.java @@ -24,7 +24,7 @@ public class InferredSpansSpanProcessorProvider implements ComponentProvider Date: Mon, 3 Nov 2025 12:14:46 +0100 Subject: [PATCH 35/36] fix --- .../InferredSpansSpanProcessorProviderTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 index a92719951..b33624561 100644 --- a/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java +++ b/inferred-spans/src/test/java/io/opentelemetry/contrib/inferredspans/InferredSpansSpanProcessorProviderTest.java @@ -8,8 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.condition.OS.WINDOWS; -import io.opentelemetry.contrib.inferredspans.internal.ProfilingActivationListener; -import io.opentelemetry.contrib.inferredspans.internal.util.OtelReflectionUtils; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; import java.io.ByteArrayInputStream; @@ -22,11 +20,19 @@ @DisabledOnOs(WINDOWS) // Uses async-profiler, which is not supported on Windows class InferredSpansSpanProcessorProviderTest { + private ProfilerTestSetup setup; + @BeforeEach + void setUp() { + setup = ProfilerTestSetup.create(c -> {}); + } + @AfterEach - public void resetGlobalOtel() { - ProfilingActivationListener.ensureInitialized(); - OtelReflectionUtils.shutdownAndResetGlobalOtel(); + void tearDown() { + if (setup != null) { + setup.close(); + } + InferredSpans.setInstance(null); } @Test From 58c4300d8fa3ec10bd570a46f73788c3f5989e12 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 3 Nov 2025 13:01:08 +0100 Subject: [PATCH 36/36] fix --- .../inferredspans/internal/SamplingProfilerTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 1e9d97807..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 @@ -57,10 +57,12 @@ void tearDown() { } @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);