diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt b/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt index 3733a382e4dd..f679900a10bf 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-spring-boot-autoconfigure.txt @@ -1,2 +1,5 @@ Comparing source compatibility of opentelemetry-spring-boot-autoconfigure-2.21.0-SNAPSHOT.jar against opentelemetry-spring-boot-autoconfigure-2.20.1.jar -No changes. \ No newline at end of file +=== UNCHANGED CLASS: PUBLIC io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + --- REMOVED ANNOTATION: org.springframework.boot.context.properties.EnableConfigurationProperties + --- REMOVED ELEMENT: value=io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtlpExporterProperties,io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties,io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelSpringProperties (-) diff --git a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts index c3010d5e8aff..8801ea1797a8 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts +++ b/instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts @@ -35,6 +35,9 @@ dependencies { annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion") implementation("javax.validation:validation-api") + // snake yaml is already used by "spring-boot-resources" + // and less likely to cause problems compared to jackson + implementation("org.snakeyaml:snakeyaml-engine") implementation(project(":instrumentation-annotations-support")) implementation(project(":instrumentation:kafka:kafka-clients:kafka-clients-2.6:library")) @@ -65,7 +68,9 @@ dependencies { library("org.springframework.boot:spring-boot-starter-data-jdbc:$springBootVersion") implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + implementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") implementation(project(":sdk-autoconfigure-support")) + implementation(project(":declarative-config-bridge")) compileOnly("io.opentelemetry:opentelemetry-extension-trace-propagators") compileOnly("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") compileOnly("io.opentelemetry:opentelemetry-exporter-logging") @@ -80,6 +85,7 @@ dependencies { testLibrary("org.springframework.boot:spring-boot-starter-test:$springBootVersion") { exclude("org.junit.vintage", "junit-vintage-engine") } + testImplementation("javax.servlet:javax.servlet-api:3.1.0") testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0") testRuntimeOnly("com.h2database:h2:1.4.197") @@ -173,6 +179,17 @@ testing { } } } + + val testDeclarativeConfig by registering(JvmTestSuite::class) { + dependencies { + implementation(project()) + implementation("io.opentelemetry:opentelemetry-sdk") + implementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") { + exclude("org.junit.vintage", "junit-vintage-engine") + } + implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") + } + } } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java new file mode 100644 index 000000000000..195782a2e1f1 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/EmbeddedConfigFile.java @@ -0,0 +1,91 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure; + +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.snakeyaml.engine.v2.api.Dump; +import org.snakeyaml.engine.v2.api.DumpSettings; +import org.snakeyaml.engine.v2.api.Load; +import org.snakeyaml.engine.v2.api.LoadSettings; +import org.springframework.boot.env.OriginTrackedMapPropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; + +class EmbeddedConfigFile { + + private EmbeddedConfigFile() { + // Utility class + } + + private static final Pattern PATTERN = + Pattern.compile( + "^Config resource 'class path resource \\[(.+)]' via location 'optional:classpath:/'$"); + + static OpenTelemetryConfigurationModel extractModel(ConfigurableEnvironment environment) + throws IOException { + for (PropertySource propertySource : environment.getPropertySources()) { + if (propertySource instanceof OriginTrackedMapPropertySource) { + return getModel(environment, (OriginTrackedMapPropertySource) propertySource); + } + } + throw new IllegalStateException("No application.yaml file found."); + } + + private static OpenTelemetryConfigurationModel getModel( + ConfigurableEnvironment environment, OriginTrackedMapPropertySource source) + throws IOException { + Matcher matcher = PATTERN.matcher(source.getName()); + if (matcher.matches()) { + String file = matcher.group(1); + + try (InputStream resourceAsStream = + environment.getClass().getClassLoader().getResourceAsStream(file)) { + if (resourceAsStream != null) { + return extractOtelConfigFile(resourceAsStream); + } else { + throw new IllegalStateException("Unable to load " + file + " in the classpath."); + } + } + } else { + throw new IllegalStateException( + "No OpenTelemetry configuration found in the application.yaml file."); + } + } + + @Nullable + @SuppressWarnings("unchecked") + private static String parseOtelNode(InputStream in) { + Load load = new Load(LoadSettings.builder().build()); + Dump dump = new Dump(DumpSettings.builder().build()); + for (Object o : load.loadAllFromInputStream(in)) { + Map data = (Map) o; + Map> otel = (Map>) data.get("otel"); + if (otel != null) { + return dump.dumpToString(otel); + } + } + throw new IllegalStateException("No 'otel' configuration found in the YAML file."); + } + + private static OpenTelemetryConfigurationModel extractOtelConfigFile(InputStream content) { + String node = parseOtelNode(content); + if (node == null || node.isEmpty()) { + throw new IllegalStateException("otel node is empty or null in the YAML file."); + } + + return DeclarativeConfiguration.parse( + new ByteArrayInputStream(node.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index b9a9bd7c9a2a..9aeca1459865 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -6,23 +6,40 @@ package io.opentelemetry.instrumentation.spring.autoconfigure; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; +import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.DeclarativeConfigDisabled; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.DeclarativeConfigEnabled; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelDisabled; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelEnabled; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelMapConverter; -import io.opentelemetry.instrumentation.spring.autoconfigure.internal.SdkEnabled; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelSpringProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtlpExporterProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.SpringConfigProperties; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources.DistroComponentProvider; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources.DistroVersionResourceProvider; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources.ResourceCustomizerProvider; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources.SpringResourceProvider; +import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.resources.Resource; +import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -33,7 +50,6 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.info.BuildProperties; @@ -41,7 +57,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; /** @@ -52,100 +68,156 @@ *

Updates the sampler probability for the configured {@link TracerProvider}. */ @Configuration -@EnableConfigurationProperties({ - OtlpExporterProperties.class, - OtelResourceProperties.class, - OtelSpringProperties.class -}) public class OpenTelemetryAutoConfiguration { private static final Logger logger = LoggerFactory.getLogger(OpenTelemetryAutoConfiguration.class); public OpenTelemetryAutoConfiguration() {} - @Bean - @ConfigurationPropertiesBinding - OtelMapConverter otelMapConverter() { - // This is needed for otlp exporter headers and OtelResourceProperties. - - // We need this converter, even if the SDK is disabled, - // because the properties are parsed before the SDK is disabled. - - // We also need this converter if the OpenTelemetry bean is user supplied, - // because the environment variables may still contain a value that needs to be converted, - // even if the SDK is disabled (and the value thus ignored). - return new OtelMapConverter(); - } - @Configuration - @Conditional(SdkEnabled.class) - @DependsOn("otelMapConverter") + @Conditional(OtelEnabled.class) @ConditionalOnMissingBean(OpenTelemetry.class) + @SuppressWarnings("OtelPrivateConstructorForUtilityClass") // spring uses reflection static class OpenTelemetrySdkConfig { - @Bean - public OpenTelemetrySdkComponentLoader openTelemetrySdkComponentLoader( - ApplicationContext applicationContext) { - return new OpenTelemetrySdkComponentLoader(applicationContext); - } + @Configuration + @EnableConfigurationProperties({ + OtlpExporterProperties.class, + OtelResourceProperties.class, + OtelSpringProperties.class + }) + @Conditional(DeclarativeConfigDisabled.class) + static class PropertiesConfig { - @Bean - public ResourceProvider otelSpringResourceProvider(Optional buildProperties) { - return new SpringResourceProvider(buildProperties); - } + @Bean + public ResourceProvider otelSpringResourceProvider( + Optional buildProperties) { + return new SpringResourceProvider(buildProperties); + } - @Bean - public ResourceProvider otelDistroVersionResourceProvider() { - return new DistroVersionResourceProvider(); - } + @Bean + public ResourceProvider otelDistroVersionResourceProvider() { + return new DistroVersionResourceProvider(); + } - @Bean - public AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk( - Environment env, - OtlpExporterProperties otlpExporterProperties, - OtelResourceProperties resourceProperties, - OtelSpringProperties otelSpringProperties, - OpenTelemetrySdkComponentLoader componentLoader) { - - return AutoConfigureUtil.setConfigPropertiesCustomizer( - AutoConfiguredOpenTelemetrySdk.builder().setComponentLoader(componentLoader), - c -> - SpringConfigProperties.create( - env, otlpExporterProperties, resourceProperties, otelSpringProperties, c)) - .build(); - } + @Bean + @ConfigurationPropertiesBinding + OtelMapConverter otelMapConverter() { + return new OtelMapConverter(); + } - @Bean - public OpenTelemetry openTelemetry( - AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { - logger.info( - "OpenTelemetry Spring Boot starter ({}) has been started", - EmbeddedInstrumentationProperties.findVersion( - "io.opentelemetry.spring-boot-autoconfigure")); - - return autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); + @Bean + public AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk( + Environment env, + OtlpExporterProperties otlpExporterProperties, + OtelResourceProperties resourceProperties, + OtelSpringProperties otelSpringProperties, + ApplicationContext applicationContext) { + + return AutoConfigureUtil.setConfigPropertiesCustomizer( + AutoConfiguredOpenTelemetrySdk.builder() + .setComponentLoader(new OpenTelemetrySdkComponentLoader(applicationContext)), + c -> + SpringConfigProperties.create( + env, otlpExporterProperties, resourceProperties, otelSpringProperties, c)) + .build(); + } + + @Bean + public OpenTelemetry openTelemetry( + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + logStart(); + return autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); + } + + @Bean + public InstrumentationConfig instrumentationConfig(ConfigProperties properties) { + return new ConfigPropertiesBridge(properties); + } + + /** + * Expose the {@link ConfigProperties} bean for use in other auto-configurations. + * + *

Not using spring boot properties directly in order to support {@link + * io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer#addPropertiesCustomizer(Function)} + * and {@link + * io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer#addPropertiesSupplier(Supplier)}. + */ + @Bean + public ConfigProperties otelProperties( + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { + return AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); + } } - /** - * Expose the {@link ConfigProperties} bean for use in other auto-configurations. - * - *

Not using spring boot properties directly in order to support {@link - * io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer#addPropertiesCustomizer(Function)} - * and {@link - * io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer#addPropertiesSupplier(Supplier)}. - */ - @Bean - public ConfigProperties otelProperties( - AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { - return AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk); + @Configuration + @Conditional(DeclarativeConfigEnabled.class) + static class EmbeddedConfigFileConfig { + + @Bean + public OpenTelemetryConfigurationModel openTelemetryConfigurationModel( + ConfigurableEnvironment environment) throws IOException { + return EmbeddedConfigFile.extractModel(environment); + } + + @Bean + public OpenTelemetry openTelemetry( + OpenTelemetryConfigurationModel model, ApplicationContext applicationContext) { + OpenTelemetrySdk sdk = + DeclarativeConfiguration.create( + model, new OpenTelemetrySdkComponentLoader(applicationContext)); + Runtime.getRuntime().addShutdownHook(new Thread(sdk::close)); + logStart(); + return sdk; + } + + @Bean + public ConfigProvider configProvider(OpenTelemetryConfigurationModel model) { + return SdkConfigProvider.create(model); + } + + /** + * Expose the {@link ConfigProperties} bean for use in other auto-configurations. + * + *

Not using spring boot properties directly, because declarative configuration does not + * integrate with spring boot properties. + */ + @Bean + public ConfigProperties otelProperties(ConfigProvider configProvider) { + return new DeclarativeConfigPropertiesBridgeBuilder() + .buildFromInstrumentationConfig(configProvider.getInstrumentationConfig()); + } + + @Bean + public InstrumentationConfig instrumentationConfig( + ConfigProperties properties, ConfigProvider configProvider) { + return new ConfigPropertiesBridge(properties, configProvider); + } + + @Bean + public DeclarativeConfigurationCustomizerProvider distroConfigurationCustomizerProvider() { + return new ResourceCustomizerProvider(); + } + + @Bean + public ComponentProvider distroComponentProvider() { + return new DistroComponentProvider(); + } } } + private static void logStart() { + logger.info( + "OpenTelemetry Spring Boot starter ({}) has been started", + EmbeddedInstrumentationProperties.findVersion( + "io.opentelemetry.spring-boot-autoconfigure")); + } + @Configuration - @DependsOn("otelMapConverter") @ConditionalOnMissingBean(OpenTelemetry.class) - @ConditionalOnProperty(name = "otel.sdk.disabled", havingValue = "true") + @Conditional(OtelDisabled.class) static class DisabledOpenTelemetrySdkConfig { + @Bean public OpenTelemetry openTelemetry() { logger.info("OpenTelemetry Spring Boot starter has been disabled"); @@ -157,6 +229,26 @@ public OpenTelemetry openTelemetry() { public ConfigProperties otelProperties() { return DefaultConfigProperties.createFromMap(Collections.emptyMap()); } + + @Bean + public InstrumentationConfig instrumentationConfig(ConfigProperties properties) { + return new ConfigPropertiesBridge(properties, null); + } + + @Configuration + @Conditional(DeclarativeConfigDisabled.class) + static class PropertiesConfig { + /** + * Is only added so that we have the same converters as with active OpenTelemetry SDK + * + *

In other words, don't break applications that (accidentally) use the {@link + * OtelMapConverter}. + */ + @Bean + OtelMapConverter otelMapConverter() { + return new OtelMapConverter(); + } + } } @Configuration @@ -170,6 +262,16 @@ public ConfigProperties otelProperties(ApplicationContext applicationContext) { } } + @Configuration + @ConditionalOnBean(OpenTelemetry.class) + @ConditionalOnMissingBean({InstrumentationConfig.class}) + static class FallbackInstrumentationConfig { + @Bean + public InstrumentationConfig instrumentationConfig(ConfigProperties properties) { + return new ConfigPropertiesBridge(properties, null); + } + } + /** * The {@link ComponentLoader} is used by the SDK autoconfiguration to load all components, e.g. * attributes = - metadata.getAnnotationAttributes(ConditionalOnEnabledInstrumentation.class.getName()); + Objects.requireNonNull( + metadata.getAnnotationAttributes(ConditionalOnEnabledInstrumentation.class.getName())); - String name = String.format("otel.instrumentation.%s.enabled", attributes.get("module")); - Boolean explicit = context.getEnvironment().getProperty(name, Boolean.class); - if (explicit != null) { - return explicit; - } - boolean defaultValue = (boolean) attributes.get("enabledByDefault"); - if (!defaultValue) { - return false; - } - return context - .getEnvironment() - .getProperty("otel.instrumentation.common.default-enabled", Boolean.class, true); + return EarlyConfig.isInstrumentationEnabled( + context.getEnvironment(), + attributes.get("module").toString(), + (boolean) attributes.get("enabledByDefault")); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/OtelDisabled.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/OtelDisabled.java new file mode 100644 index 000000000000..d521f76eb7ba --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/OtelDisabled.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class OtelDisabled implements Condition { + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return !EarlyConfig.otelEnabled(context.getEnvironment()); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/OtelEnabled.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/OtelEnabled.java new file mode 100644 index 000000000000..a1b87384e8ce --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/OtelEnabled.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class OtelEnabled implements Condition { + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return EarlyConfig.otelEnabled(context.getEnvironment()); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/SdkEnabled.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/SdkEnabled.java deleted file mode 100644 index 082bdebc00b7..000000000000 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/SdkEnabled.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.autoconfigure.internal; - -import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public class SdkEnabled extends AnyNestedCondition { - public SdkEnabled() { - super(ConfigurationPhase.PARSE_CONFIGURATION); - } - - @ConditionalOnProperty(name = "otel.sdk.disabled", havingValue = "false", matchIfMissing = true) - static class NotDisabled {} -} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java index a1a615a31fba..88baae305c96 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java @@ -7,9 +7,9 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; @@ -28,13 +28,13 @@ final class DataSourcePostProcessor implements BeanPostProcessor, Ordered { private static final Class ROUTING_DATA_SOURCE_CLASS = getRoutingDataSourceClass(); private final ObjectProvider openTelemetryProvider; - private final ObjectProvider configPropertiesProvider; + private final ObjectProvider configProvider; DataSourcePostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { this.openTelemetryProvider = openTelemetryProvider; - this.configPropertiesProvider = configPropertiesProvider; + this.configProvider = configProvider; } private static Class getRoutingDataSourceClass() { @@ -57,22 +57,18 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { && !isRoutingDatasource(bean) && !ScopedProxyUtils.isScopedTarget(beanName)) { DataSource dataSource = (DataSource) bean; + InstrumentationConfig config = configProvider.getObject(); DataSource otelDataSource = JdbcTelemetry.builder(openTelemetryProvider.getObject()) .setStatementSanitizationEnabled( InstrumentationConfigUtil.isStatementSanitizationEnabled( - configPropertiesProvider.getObject(), - "otel.instrumentation.jdbc.statement-sanitizer.enabled")) + config, "otel.instrumentation.jdbc.statement-sanitizer.enabled")) .setCaptureQueryParameters( - configPropertiesProvider - .getObject() - .getBoolean( - "otel.instrumentation.jdbc.experimental.capture-query-parameters", false)) + config.getBoolean( + "otel.instrumentation.jdbc.experimental.capture-query-parameters", false)) .setTransactionInstrumenterEnabled( - configPropertiesProvider - .getObject() - .getBoolean( - "otel.instrumentation.jdbc.experimental.transaction.enabled", false)) + config.getBoolean( + "otel.instrumentation.jdbc.experimental.transaction.enabled", false)) .build() .wrap(dataSource); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfiguration.java index 3ec55a5a06c2..1732c3be178d 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfiguration.java @@ -6,8 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.jdbc; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import javax.sql.DataSource; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -33,7 +33,7 @@ public JdbcInstrumentationAutoConfiguration() {} // static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning static DataSourcePostProcessor dataSourcePostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { - return new DataSourcePostProcessor(openTelemetryProvider, configPropertiesProvider); + ObjectProvider configProvider) { + return new DataSourcePostProcessor(openTelemetryProvider, configProvider); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java index 44f845e25ff1..4df611685733 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java @@ -6,10 +6,10 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.kafka; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -42,10 +42,10 @@ DefaultKafkaProducerFactoryCustomizer otelKafkaProducerFactoryCustomizer( @Bean static SpringKafkaTelemetry getTelemetry( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { return SpringKafkaTelemetry.builder(openTelemetryProvider.getObject()) .setCaptureExperimentalSpanAttributes( - configPropertiesProvider + configProvider .getObject() .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) .build(); @@ -60,8 +60,8 @@ static SpringKafkaTelemetry getTelemetry( static ConcurrentKafkaListenerContainerFactoryPostProcessor otelKafkaListenerContainerFactoryBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { return new ConcurrentKafkaListenerContainerFactoryPostProcessor( - () -> getTelemetry(openTelemetryProvider, configPropertiesProvider)); + () -> getTelemetry(openTelemetryProvider, configProvider)); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/DeclarativeConfigLoggingExporterAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/DeclarativeConfigLoggingExporterAutoConfiguration.java new file mode 100644 index 000000000000..a5cf0ea7b72e --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/DeclarativeConfigLoggingExporterAutoConfiguration.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging; + +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.instrumentation.logging.internal.AbstractSpanLoggingCustomizerProvider; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelEnabled; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +@Conditional(OtelEnabled.class) +@ConditionalOnClass(LoggingSpanExporter.class) +@Configuration +public class DeclarativeConfigLoggingExporterAutoConfiguration { + @Bean + public DeclarativeConfigurationCustomizerProvider spanLoggingCustomizerProvider() { + return new SpanLoggingCustomizerProvider(); + } + + static class SpanLoggingCustomizerProvider extends AbstractSpanLoggingCustomizerProvider { + @Override + protected boolean isEnabled(OpenTelemetryConfigurationModel model) { + DeclarativeConfigProperties instrumentation = + SdkConfigProvider.create(model).getInstrumentationConfig(); + if (instrumentation == null) { + return false; + } + + return instrumentation + .getStructured("java", empty()) + .getStructured("spring_starter", empty()) + .getBoolean("debug", false); + } + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/LogbackAppenderInstaller.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/LogbackAppenderInstaller.java index fd13848aaf2f..51588f786d25 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/LogbackAppenderInstaller.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/LogbackAppenderInstaller.java @@ -9,12 +9,14 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.EarlyConfig; import java.util.Iterator; import java.util.Optional; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.core.env.ConfigurableEnvironment; class LogbackAppenderInstaller { @@ -40,25 +42,18 @@ static void install(ApplicationEnvironmentPreparedEvent applicationEnvironmentPr } } - private static boolean isLogbackAppenderAddable( - ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) { - return isAppenderAddable( - applicationEnvironmentPreparedEvent, "otel.instrumentation.logback-appender.enabled"); + private static boolean isLogbackAppenderAddable(ApplicationEnvironmentPreparedEvent event) { + return isAppenderAddable(event, "logback-appender"); } - private static boolean isLogbackMdcAppenderAddable( - ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) { - return isAppenderAddable( - applicationEnvironmentPreparedEvent, "otel.instrumentation.logback-mdc.enabled"); + private static boolean isLogbackMdcAppenderAddable(ApplicationEnvironmentPreparedEvent event) { + return isAppenderAddable(event, "logback-mdc"); } - private static boolean isAppenderAddable( - ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent, String property) { - boolean otelSdkDisabled = - evaluateBooleanProperty(applicationEnvironmentPreparedEvent, "otel.sdk.disabled", false); - boolean logbackInstrumentationEnabled = - evaluateBooleanProperty(applicationEnvironmentPreparedEvent, property, true); - return !otelSdkDisabled && logbackInstrumentationEnabled; + private static boolean isAppenderAddable(ApplicationEnvironmentPreparedEvent event, String name) { + ConfigurableEnvironment environment = event.getEnvironment(); + return EarlyConfig.otelEnabled(environment) + && EarlyConfig.isInstrumentationEnabled(environment, name, true); } private static void reInitializeOpenTelemetryAppender( @@ -224,18 +219,9 @@ private static void initializeMdcAppenderFromProperties( private static Boolean evaluateBooleanProperty( ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent, String property) { - return applicationEnvironmentPreparedEvent - .getEnvironment() - .getProperty(property, Boolean.class); - } - - private static boolean evaluateBooleanProperty( - ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent, - String property, - boolean defaultValue) { - return applicationEnvironmentPreparedEvent - .getEnvironment() - .getProperty(property, Boolean.class, defaultValue); + ConfigurableEnvironment environment = applicationEnvironmentPreparedEvent.getEnvironment(); + return environment.getProperty( + EarlyConfig.translatePropertyName(environment, property), Boolean.class); } private static Optional findAppender(Class appenderClass) { diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java index 39a56a8b07c4..2c370a8afc49 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java @@ -7,10 +7,10 @@ import com.mongodb.MongoClientSettings; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.mongo.MongoClientSettingsBuilderCustomizer; @@ -29,7 +29,7 @@ public class MongoClientInstrumentationAutoConfiguration { @Bean MongoClientSettingsBuilderCustomizer customizer( - OpenTelemetry openTelemetry, ConfigProperties config) { + OpenTelemetry openTelemetry, InstrumentationConfig config) { return builder -> builder.addCommandListener( MongoTelemetry.builder(openTelemetry) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentationAutoConfiguration.java index d744b8fe1dc4..254297f2eafa 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentationAutoConfiguration.java @@ -6,8 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.r2dbc; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.r2dbc.spi.ConnectionFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -31,7 +31,7 @@ public R2dbcInstrumentationAutoConfiguration() {} // static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning static R2dbcInstrumentingPostProcessor r2dbcInstrumentingPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { - return new R2dbcInstrumentingPostProcessor(openTelemetryProvider, configPropertiesProvider); + ObjectProvider configProvider) { + return new R2dbcInstrumentingPostProcessor(openTelemetryProvider, configProvider); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java index 5d75b28946ef..09849deef13b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java @@ -6,9 +6,9 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.r2dbc; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.r2dbc.v1_0.internal.shaded.R2dbcTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactoryOptions; import org.springframework.aop.scope.ScopedProxyUtils; @@ -19,13 +19,13 @@ class R2dbcInstrumentingPostProcessor implements BeanPostProcessor { private final ObjectProvider openTelemetryProvider; - private final ObjectProvider configPropertiesProvider; + private final ObjectProvider configProvider; R2dbcInstrumentingPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { this.openTelemetryProvider = openTelemetryProvider; - this.configPropertiesProvider = configPropertiesProvider; + this.configProvider = configProvider; } @Override @@ -35,7 +35,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { return R2dbcTelemetry.builder(openTelemetryProvider.getObject()) .setStatementSanitizationEnabled( InstrumentationConfigUtil.isStatementSanitizationEnabled( - configPropertiesProvider.getObject(), + configProvider.getObject(), "otel.instrumentation.r2dbc.statement-sanitizer.enabled")) .build() .wrapConnectionFactory(connectionFactory, getConnectionFactoryOptions(connectionFactory)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/runtimemetrics/RuntimeMetricsAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/runtimemetrics/RuntimeMetricsAutoConfiguration.java index f58f4da5f217..e88d02ac0c62 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/runtimemetrics/RuntimeMetricsAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/runtimemetrics/RuntimeMetricsAutoConfiguration.java @@ -6,9 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.Comparator; import java.util.Optional; import javax.annotation.PreDestroy; @@ -45,8 +44,7 @@ public void stopMetrics() throws Exception { public void handleApplicationReadyEvent(ApplicationReadyEvent event) { ConfigurableApplicationContext applicationContext = event.getApplicationContext(); OpenTelemetry openTelemetry = applicationContext.getBean(OpenTelemetry.class); - ConfigPropertiesBridge config = - new ConfigPropertiesBridge(applicationContext.getBean(ConfigProperties.class)); + InstrumentationConfig config = applicationContext.getBean(InstrumentationConfig.class); double version = Math.max(8, Double.parseDouble(System.getProperty("java.specification.version"))); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java index 5dfec2280e8e..6d84cde77985 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAspect.java @@ -9,6 +9,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor; @@ -16,7 +17,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -43,7 +43,7 @@ final class SpringSchedulingInstrumentationAspect { private final Instrumenter instrumenter; public SpringSchedulingInstrumentationAspect( - OpenTelemetry openTelemetry, ConfigProperties configProperties) { + OpenTelemetry openTelemetry, InstrumentationConfig config) { CodeAttributesGetter codedAttributesGetter = ClassAndMethod.codeAttributesGetter(); InstrumenterBuilder builder = @@ -52,7 +52,7 @@ public SpringSchedulingInstrumentationAspect( INSTRUMENTATION_NAME, CodeSpanNameExtractor.create(codedAttributesGetter)) .addAttributesExtractor(CodeAttributesExtractor.create(codedAttributesGetter)); - if (configProperties.getBoolean( + if (config.getBoolean( "otel.instrumentation.spring-scheduling.experimental-span-attributes", false)) { builder.addAttributesExtractor( AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "spring_scheduling")); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java index 8135bead9c0b..2d5e0fba7473 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SpringSchedulingInstrumentationAutoConfiguration.java @@ -6,8 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.aspectj.lang.annotation.Aspect; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -28,7 +28,7 @@ class SpringSchedulingInstrumentationAutoConfiguration { @Bean SpringSchedulingInstrumentationAspect springSchedulingInstrumentationAspect( - OpenTelemetry openTelemetry, ConfigProperties configProperties) { - return new SpringSchedulingInstrumentationAspect(openTelemetry, configProperties); + OpenTelemetry openTelemetry, InstrumentationConfig config) { + return new SpringSchedulingInstrumentationAspect(openTelemetry, config); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateBeanPostProcessor.java index 0cda4b63d686..4db9b873fece 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateBeanPostProcessor.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.web.client.RestTemplate; @@ -15,13 +15,13 @@ final class RestTemplateBeanPostProcessor implements BeanPostProcessor { private final ObjectProvider openTelemetryProvider; - private final ObjectProvider configPropertiesProvider; + private final ObjectProvider configProvider; RestTemplateBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { this.openTelemetryProvider = openTelemetryProvider; - this.configPropertiesProvider = configPropertiesProvider; + this.configProvider = configProvider; } @Override @@ -31,8 +31,6 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { } return RestTemplateInstrumentation.addIfNotPresent( - (RestTemplate) bean, - openTelemetryProvider.getObject(), - configPropertiesProvider.getObject()); + (RestTemplate) bean, openTelemetryProvider.getObject(), configProvider.getObject()); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java index 9465c534d0ce..ca4c0c97c466 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestTemplateInstrumentation.java @@ -7,10 +7,10 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.web.v3_1.SpringWebTelemetry; import io.opentelemetry.instrumentation.spring.web.v3_1.internal.WebTelemetryUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.List; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; @@ -21,7 +21,7 @@ private RestTemplateInstrumentation() {} @CanIgnoreReturnValue static RestTemplate addIfNotPresent( - RestTemplate restTemplate, OpenTelemetry openTelemetry, ConfigProperties config) { + RestTemplate restTemplate, OpenTelemetry openTelemetry, InstrumentationConfig config) { ClientHttpRequestInterceptor instrumentationInterceptor = InstrumentationConfigUtil.configureClientBuilder( diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfiguration.java index 21a18ec8db2b..97d5bd9b52d5 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfiguration.java @@ -6,8 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.web.client.RestTemplateCustomizer; @@ -34,16 +34,16 @@ public SpringWebInstrumentationAutoConfiguration() {} @Bean static RestTemplateBeanPostProcessor otelRestTemplateBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { - return new RestTemplateBeanPostProcessor(openTelemetryProvider, configPropertiesProvider); + ObjectProvider configProvider) { + return new RestTemplateBeanPostProcessor(openTelemetryProvider, configProvider); } @Bean RestTemplateCustomizer otelRestTemplateCustomizer( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { return restTemplate -> RestTemplateInstrumentation.addIfNotPresent( - restTemplate, openTelemetryProvider.getObject(), configPropertiesProvider.getObject()); + restTemplate, openTelemetryProvider.getObject(), configProvider.getObject()); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java index ff6d8f9a5d0b..79bacde3ccc2 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java @@ -6,8 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -34,12 +34,12 @@ public SpringWebfluxInstrumentationAutoConfiguration() {} @Bean static WebClientBeanPostProcessor otelWebClientBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { - return new WebClientBeanPostProcessor(openTelemetryProvider, configPropertiesProvider); + ObjectProvider configProvider) { + return new WebClientBeanPostProcessor(openTelemetryProvider, configProvider); } @Bean - WebFilter telemetryFilter(OpenTelemetry openTelemetry, ConfigProperties config) { + WebFilter telemetryFilter(OpenTelemetry openTelemetry, InstrumentationConfig config) { return WebClientBeanPostProcessor.getWebfluxServerTelemetry(openTelemetry, config) .createWebFilterAndRegisterReactorHook(); } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java index 13a74048d3eb..9b501c410be3 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java @@ -6,11 +6,11 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxClientTelemetry; import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxServerTelemetry; import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxBuilderUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.web.reactive.function.client.WebClient; @@ -23,17 +23,17 @@ final class WebClientBeanPostProcessor implements BeanPostProcessor { private final ObjectProvider openTelemetryProvider; - private final ObjectProvider configPropertiesProvider; + private final ObjectProvider configProvider; WebClientBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { this.openTelemetryProvider = openTelemetryProvider; - this.configPropertiesProvider = configPropertiesProvider; + this.configProvider = configProvider; } static SpringWebfluxClientTelemetry getWebfluxClientTelemetry( - OpenTelemetry openTelemetry, ConfigProperties config) { + OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureClientBuilder( config, SpringWebfluxClientTelemetry.builder(openTelemetry), @@ -42,7 +42,7 @@ static SpringWebfluxClientTelemetry getWebfluxClientTelemetry( } static SpringWebfluxServerTelemetry getWebfluxServerTelemetry( - OpenTelemetry openTelemetry, ConfigProperties config) { + OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureServerBuilder( config, SpringWebfluxServerTelemetry.builder(openTelemetry), @@ -64,8 +64,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { private WebClient.Builder wrapBuilder(WebClient.Builder webClientBuilder) { SpringWebfluxClientTelemetry instrumentation = - getWebfluxClientTelemetry( - openTelemetryProvider.getObject(), configPropertiesProvider.getObject()); + getWebfluxClientTelemetry(openTelemetryProvider.getObject(), configProvider.getObject()); return webClientBuilder.filters(instrumentation::addFilter); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java index 8d971c9d4890..73d3f657504c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java @@ -6,11 +6,11 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webmvc.v5_3.SpringWebMvcTelemetry; import io.opentelemetry.instrumentation.spring.webmvc.v5_3.internal.SpringMvcBuilderUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import javax.servlet.Filter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -28,7 +28,7 @@ public class SpringWebMvc5InstrumentationAutoConfiguration { @Bean - Filter otelWebMvcFilter(OpenTelemetry openTelemetry, ConfigProperties config) { + Filter otelWebMvcFilter(OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureServerBuilder( config, SpringWebMvcTelemetry.builder(openTelemetry), diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/ConfigPropertiesBridge.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/ConfigPropertiesBridge.java index 047d2ee809ea..0f840594847e 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/ConfigPropertiesBridge.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/ConfigPropertiesBridge.java @@ -5,8 +5,11 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties; +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; @@ -24,9 +27,16 @@ public final class ConfigPropertiesBridge implements InstrumentationConfig { private final ConfigProperties configProperties; + @Nullable private final ConfigProvider configProvider; public ConfigPropertiesBridge(ConfigProperties configProperties) { + this(configProperties, null); + } + + public ConfigPropertiesBridge( + ConfigProperties configProperties, @Nullable ConfigProvider configProvider) { this.configProperties = configProperties; + this.configProvider = configProvider; } @Nullable @@ -113,19 +123,25 @@ public Map getMap(String name, Map defaultValue) @Override public boolean isDeclarative() { - return false; + return configProvider != null; } @Override public DeclarativeConfigProperties getDeclarativeConfig(String node) { - throw new IllegalStateException( - "Declarative configuration is not supported in spring boot autoconfigure yet"); + DeclarativeConfigProperties config = + InstrumentationConfigUtil.javaInstrumentationConfig(configProvider, node); + if (config == null) { + // there is no declarative config for this node + // this needs to be a different value than null to avoid confusion with + // the case when declarative config is not supported at all + return empty(); + } + return config; } @Nullable @Override public ConfigProvider getConfigProvider() { - // declarative config support will be added in the future - return null; + return configProvider; } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java index fb5e582bfaa5..683529a18b29 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java @@ -9,7 +9,7 @@ import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import java.util.function.Function; /** @@ -21,7 +21,7 @@ private InstrumentationConfigUtil() {} @CanIgnoreReturnValue public static T configureClientBuilder( - ConfigProperties config, + InstrumentationConfig config, T builder, Function> getBuilder) { getBuilder.apply(builder).configure(getConfig(config)); @@ -30,18 +30,18 @@ public static T configureClientBuilder( @CanIgnoreReturnValue public static T configureServerBuilder( - ConfigProperties config, + InstrumentationConfig config, T builder, Function> getBuilder) { getBuilder.apply(builder).configure(getConfig(config)); return builder; } - private static CommonConfig getConfig(ConfigProperties config) { - return new CommonConfig(new ConfigPropertiesBridge(config)); + private static CommonConfig getConfig(InstrumentationConfig config) { + return new CommonConfig(config); } - public static boolean isStatementSanitizationEnabled(ConfigProperties config, String key) { + public static boolean isStatementSanitizationEnabled(InstrumentationConfig config, String key) { return config.getBoolean( key, config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true)); } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroComponentProvider.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroComponentProvider.java new file mode 100644 index 000000000000..ad1a669067cd --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroComponentProvider.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +@SuppressWarnings("rawtypes") +public class DistroComponentProvider implements ComponentProvider { + + @Override + public Class getType() { + return Resource.class; + } + + @Override + public String getName() { + return "opentelemetry-spring-boot-starter"; + } + + @Override + public Resource create(DeclarativeConfigProperties config) { + return DistroVersionResourceProvider.get(); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroVersionResourceProvider.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroVersionResourceProvider.java index 8ba185f97b98..db66ce9f0220 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroVersionResourceProvider.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/DistroVersionResourceProvider.java @@ -28,6 +28,10 @@ public class DistroVersionResourceProvider implements ResourceProvider { @Override public Resource createResource(ConfigProperties config) { + return get(); + } + + static Resource get() { return Resource.create( Attributes.of( TELEMETRY_DISTRO_NAME, diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/ResourceCustomizerProvider.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/ResourceCustomizerProvider.java new file mode 100644 index 000000000000..1bbd1af4f5d2 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/resources/ResourceCustomizerProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.resources; + +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.ExperimentalResourceDetectionModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalResourceDetectorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class ResourceCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { + + // opentelemetry-javaagent-distribution: adds "distro.name" and "distro.version" attributes + // (DistroComponentProvider in this package) + private static final List REQUIRED_DETECTORS = + Collections.singletonList("opentelemetry-spring-boot-starter"); + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + ResourceModel resource = model.getResource(); + if (resource == null) { + resource = new ResourceModel(); + model.withResource(resource); + } + ExperimentalResourceDetectionModel detectionModel = resource.getDetectionDevelopment(); + if (detectionModel == null) { + detectionModel = new ExperimentalResourceDetectionModel(); + resource.withDetectionDevelopment(detectionModel); + } + List detectors = + Objects.requireNonNull(detectionModel.getDetectors()); + Set names = + detectors.stream() + .flatMap(detector -> detector.getAdditionalProperties().keySet().stream()) + .collect(Collectors.toSet()); + + for (String name : REQUIRED_DETECTORS) { + if (!names.contains(name)) { + ExperimentalResourceDetectorModel detector = new ExperimentalResourceDetectorModel(); + detector.getAdditionalProperties().put(name, null); + // add first (the least precedence) + // so that the user can add a differently named detector that takes precedence + detectors.add(0, detector); + } + } + return model; + }); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java index 8ebefdd655f6..9ef5b7f19194 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientBeanPostProcessor.java @@ -6,10 +6,10 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.web.v3_1.SpringWebTelemetry; import io.opentelemetry.instrumentation.spring.web.v3_1.internal.WebTelemetryUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.http.client.ClientHttpRequestInterceptor; @@ -18,26 +18,26 @@ final class RestClientBeanPostProcessor implements BeanPostProcessor { private final ObjectProvider openTelemetryProvider; - private final ObjectProvider configPropertiesProvider; + private final ObjectProvider configProvider; public RestClientBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { this.openTelemetryProvider = openTelemetryProvider; - this.configPropertiesProvider = configPropertiesProvider; + this.configProvider = configProvider; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof RestClient restClient) { return addRestClientInterceptorIfNotPresent( - restClient, openTelemetryProvider.getObject(), configPropertiesProvider.getObject()); + restClient, openTelemetryProvider.getObject(), configProvider.getObject()); } return bean; } private static RestClient addRestClientInterceptorIfNotPresent( - RestClient restClient, OpenTelemetry openTelemetry, ConfigProperties config) { + RestClient restClient, OpenTelemetry openTelemetry, InstrumentationConfig config) { ClientHttpRequestInterceptor instrumentationInterceptor = getInterceptor(openTelemetry, config); return restClient @@ -55,7 +55,7 @@ private static RestClient addRestClientInterceptorIfNotPresent( } static ClientHttpRequestInterceptor getInterceptor( - OpenTelemetry openTelemetry, ConfigProperties config) { + OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureClientBuilder( config, SpringWebTelemetry.builder(openTelemetry), diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfiguration.java index a0fad74b0a35..72c83ef5d5ad 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfiguration.java @@ -6,8 +6,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -34,17 +34,17 @@ public class RestClientInstrumentationAutoConfiguration { @Bean static RestClientBeanPostProcessor otelRestClientBeanPostProcessor( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { - return new RestClientBeanPostProcessor(openTelemetryProvider, configPropertiesProvider); + ObjectProvider configProvider) { + return new RestClientBeanPostProcessor(openTelemetryProvider, configProvider); } @Bean RestClientCustomizer otelRestClientCustomizer( ObjectProvider openTelemetryProvider, - ObjectProvider configPropertiesProvider) { + ObjectProvider configProvider) { return builder -> builder.requestInterceptor( RestClientBeanPostProcessor.getInterceptor( - openTelemetryProvider.getObject(), configPropertiesProvider.getObject())); + openTelemetryProvider.getObject(), configProvider.getObject())); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java index 2dc83966bc5f..b8653d4d3936 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java @@ -6,11 +6,11 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webmvc.v6_0.SpringWebMvcTelemetry; import io.opentelemetry.instrumentation.spring.webmvc.v6_0.internal.SpringMvcBuilderUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import jakarta.servlet.Filter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -28,7 +28,7 @@ public class SpringWebMvc6InstrumentationAutoConfiguration { @Bean - Filter otelWebMvcFilter(OpenTelemetry openTelemetry, ConfigProperties config) { + Filter otelWebMvcFilter(OpenTelemetry openTelemetry, InstrumentationConfig config) { return InstrumentationConfigUtil.configureServerBuilder( config, SpringWebMvcTelemetry.builder(openTelemetry), diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java index 1e1a04bb5f10..be29f798f579 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java @@ -26,7 +26,7 @@ /** Spring Boot auto configuration test for {@link OpenTelemetryAutoConfiguration}. */ class OpenTelemetryAutoConfigurationTest { @TestConfiguration - static class CustomTracerConfiguration { + static class CustomOtelConfiguration { @Bean public OpenTelemetry customOpenTelemetry() { return OpenTelemetry.noop(); @@ -48,7 +48,7 @@ public OpenTelemetry customOpenTelemetry() { "when Application Context contains OpenTelemetry bean should NOT initialize openTelemetry") void customOpenTelemetry() { this.contextRunner - .withUserConfiguration(CustomTracerConfiguration.class) + .withUserConfiguration(CustomOtelConfiguration.class) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class)) .run( context -> @@ -152,10 +152,8 @@ void shouldInitializeSdkWhenNotDisabled() { .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class)) .withPropertyValues("otel.sdk.disabled=false") .run( - context -> { - assertThat(context).getBean("openTelemetry").isInstanceOf(OpenTelemetrySdk.class); - assertThat(context).hasBean("openTelemetry"); - }); + context -> + assertThat(context).getBean("openTelemetry").isInstanceOf(OpenTelemetrySdk.class)); } @Test diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfigurationTest.java index e269ea83693c..a81051228f98 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/JdbcInstrumentationAutoConfigurationTest.java @@ -10,8 +10,9 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.sql.Connection; import java.sql.Statement; @@ -33,8 +34,10 @@ class JdbcInstrumentationAutoConfigurationTest { private final ApplicationContextRunner runner = new ApplicationContextRunner() .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withConfiguration( AutoConfigurations.of( JdbcInstrumentationAutoConfiguration.class, DataSourceAutoConfiguration.class)) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/SpanLoggingCustomizerProviderTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/SpanLoggingCustomizerProviderTest.java new file mode 100644 index 000000000000..7328adcfedb8 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/logging/SpanLoggingCustomizerProviderTest.java @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging; + +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 java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class SpanLoggingCustomizerProviderTest { + + @ParameterizedTest + @CsvSource({ + "true, false, true", + "false, false, false", + ", false, false", // empty value means property is not set + "invalid, false, false", + "true, true, true", + }) + void addSpanLoggingExporter(String propertyValue, boolean alreadyAdded, boolean expected) { + String debug = + propertyValue == null + ? "" + : "instrumentation/development: \n" + + " java: \n" + + " spring_starter: \n" + + " debug: " + + propertyValue; + + String yaml = + alreadyAdded + ? "file_format: \"1.0-rc.1\"\n" + + "tracer_provider:\n" + + " processors:\n" + + " - simple:\n" + + " exporter:\n" + + " console: {}\n" + : "file_format: \"1.0-rc.1\"\n" + debug; + + OpenTelemetryConfigurationModel model = + applyCustomizer( + DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))), + new DeclarativeConfigLoggingExporterAutoConfiguration.SpanLoggingCustomizerProvider()); + + String console = "ConsoleExporterModel"; + if (expected) { + assertThat(model.toString()).containsOnlyOnce(console); + } else { + assertThat(model.toString()).doesNotContain(console); + } + } + + private static OpenTelemetryConfigurationModel applyCustomizer( + OpenTelemetryConfigurationModel model, + DeclarativeConfigLoggingExporterAutoConfiguration.SpanLoggingCustomizerProvider provider) { + List> customizers = + new ArrayList<>(); + provider.customize(c -> customizers.add(c)); + for (Function customizer : + customizers) { + model = customizer.apply(model); + } + return model; + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2DbcInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2DbcInstrumentationAutoConfigurationTest.java index ffdd3c7d0181..bed063c28d1f 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2DbcInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/r2dbc/R2DbcInstrumentationAutoConfigurationTest.java @@ -9,8 +9,9 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -28,8 +29,10 @@ class R2DbcInstrumentationAutoConfigurationTest { private final ApplicationContextRunner runner = new ApplicationContextRunner() .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withConfiguration( AutoConfigurations.of( R2dbcInstrumentationAutoConfiguration.class, R2dbcAutoConfiguration.class)) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java index d5fdb35b2bb8..3306b280869c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAspectTest.java @@ -13,9 +13,10 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.StatusData; @@ -39,8 +40,8 @@ class SchedulingInstrumentationAspectTest { private String unproxiedTesterClassName; SpringSchedulingInstrumentationAspect newAspect( - OpenTelemetry openTelemetry, ConfigProperties configProperties) { - return new SpringSchedulingInstrumentationAspect(openTelemetry, configProperties); + OpenTelemetry openTelemetry, InstrumentationConfig config) { + return new SpringSchedulingInstrumentationAspect(openTelemetry, config); } @BeforeEach @@ -56,7 +57,8 @@ void setup() { SpringSchedulingInstrumentationAspect aspect = newAspect( testing.getOpenTelemetry(), - DefaultConfigProperties.createFromMap(Collections.emptyMap())); + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))); factory.addAspect(aspect); schedulingTester = factory.getProxy(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAutoConfigurationTest.java index 37d91b411d5a..5b631daf1434 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/scheduling/SchedulingInstrumentationAutoConfigurationTest.java @@ -8,7 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -20,8 +21,10 @@ class SchedulingInstrumentationAutoConfigurationTest { new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withConfiguration( AutoConfigurations.of(SpringSchedulingInstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfigurationTest.java index 53bbd882a6d2..8e08dec0ea20 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/SpringWebInstrumentationAutoConfigurationTest.java @@ -8,7 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -22,8 +23,10 @@ class SpringWebInstrumentationAutoConfigurationTest { new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withBean(RestTemplate.class, RestTemplate::new) .withConfiguration( AutoConfigurations.of(SpringWebInstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java index d75814732785..df6273d4686c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java @@ -8,7 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -21,8 +22,10 @@ class SpringWebfluxInstrumentationAutoConfigurationTest { new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withConfiguration( AutoConfigurations.of(SpringWebfluxInstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java index 821f0fbfb647..4c25da12a112 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessorTest.java @@ -8,7 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import org.junit.jupiter.api.DisplayName; @@ -24,7 +25,8 @@ class WebClientBeanPostProcessorTest { static { beanFactory.registerSingleton("openTelemetry", OpenTelemetry.noop()); beanFactory.registerSingleton( - "configProperties", DefaultConfigProperties.createFromMap(Collections.emptyMap())); + "configProperties", + new ConfigPropertiesBridge(DefaultConfigProperties.createFromMap(Collections.emptyMap()))); } @Test @@ -34,7 +36,7 @@ void returnsObject() { BeanPostProcessor underTest = new WebClientBeanPostProcessor( beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(ConfigProperties.class)); + beanFactory.getBeanProvider(InstrumentationConfig.class)); assertThat(underTest.postProcessAfterInitialization(new Object(), "testObject")) .isExactlyInstanceOf(Object.class); @@ -46,7 +48,7 @@ void returnsWebClient() { BeanPostProcessor underTest = new WebClientBeanPostProcessor( beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(ConfigProperties.class)); + beanFactory.getBeanProvider(InstrumentationConfig.class)); assertThat(underTest.postProcessAfterInitialization(WebClient.create(), "testWebClient")) .isInstanceOf(WebClient.class); @@ -58,7 +60,7 @@ void returnsWebClientBuilder() { BeanPostProcessor underTest = new WebClientBeanPostProcessor( beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(ConfigProperties.class)); + beanFactory.getBeanProvider(InstrumentationConfig.class)); assertThat( underTest.postProcessAfterInitialization(WebClient.builder(), "testWebClientBuilder")) @@ -71,7 +73,7 @@ void addsExchangeFilterWebClient() { BeanPostProcessor underTest = new WebClientBeanPostProcessor( beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(ConfigProperties.class)); + beanFactory.getBeanProvider(InstrumentationConfig.class)); WebClient webClient = WebClient.create(); Object processedWebClient = @@ -96,7 +98,7 @@ void addsExchangeFilterWebClientBuilder() { BeanPostProcessor underTest = new WebClientBeanPostProcessor( beanFactory.getBeanProvider(OpenTelemetry.class), - beanFactory.getBeanProvider(ConfigProperties.class)); + beanFactory.getBeanProvider(InstrumentationConfig.class)); WebClient.Builder webClientBuilder = WebClient.builder(); underTest.postProcessAfterInitialization(webClientBuilder, "testWebClientBuilder"); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java index 62112774516e..3c90a46fdfdc 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java @@ -9,7 +9,8 @@ import static org.junit.jupiter.api.Assumptions.assumeFalse; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import javax.servlet.Filter; @@ -24,8 +25,10 @@ class SpringWebMvcInstrumentation5AutoConfigurationTest { new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withConfiguration( AutoConfigurations.of(SpringWebMvc5InstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java index 02433342b1ab..96c0245747d7 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/SpringConfigPropertiesTest.java @@ -86,9 +86,9 @@ void mapFlatHeadersWithUserSuppliedOtelBean(String key) { .withSystemProperties(key + "=a=1,b=2") .withBean(OpenTelemetry.class, OpenTelemetry::noop) .run( - context -> - assertThat(getConfig(context).getMap(key)) - .containsExactly(entry("a", "1"), entry("b", "2"))); + context -> { + // don't crash if OpenTelemetry bean is provided + }); } @ParameterizedTest diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/testDeclarativeConfig/java/io/opentelemetry/instrumentation/spring/autoconfigure/DeclarativeConfigTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/testDeclarativeConfig/java/io/opentelemetry/instrumentation/spring/autoconfigure/DeclarativeConfigTest.java new file mode 100644 index 000000000000..d15468a17944 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/testDeclarativeConfig/java/io/opentelemetry/instrumentation/spring/autoconfigure/DeclarativeConfigTest.java @@ -0,0 +1,134 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.SpringWebInstrumentationAutoConfiguration; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; + +/** Spring Boot auto configuration test for {@link OpenTelemetryAutoConfiguration}. */ +class DeclarativeConfigTest { + @TestConfiguration + static class CustomTracerConfiguration { + @Bean + public OpenTelemetry customOpenTelemetry() { + return OpenTelemetry.noop(); + } + } + + private final ApplicationContextRunner contextRunner = + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class)) + // to load application.yaml + .withInitializer(new ConfigDataApplicationContextInitializer()); + + @Test + @DisplayName( + "when Application Context contains OpenTelemetry bean should NOT initialize openTelemetry") + void customOpenTelemetry() { + this.contextRunner + .withUserConfiguration(CustomTracerConfiguration.class) + .withPropertyValues("otel.file_format=1.0-rc.1") + .run( + context -> + assertThat(context) + .hasBean("customOpenTelemetry") + .doesNotHaveBean("openTelemetry") + .hasBean("otelProperties")); + } + + @Test + @DisplayName( + "when Application Context DOES NOT contain OpenTelemetry bean should initialize openTelemetry") + void initializeProvidersAndOpenTelemetry() { + this.contextRunner.run( + context -> + assertThat(context) + .hasBean("openTelemetry") + .hasBean("otelProperties") + .getBean(InstrumentationConfig.class) + .isNotNull() + .satisfies( + c -> + assertThat(c.getDeclarativeConfig("foo")) + .isNotNull() + .satisfies( + instrumentationConfig -> + assertThat(instrumentationConfig.getString("bar")) + .isEqualTo("baz")))); + } + + @Test + void shouldInitializeSdkWhenNotDisabled() { + this.contextRunner + .withPropertyValues("otel.file_format=1.0-rc.1", "otel.disabled=false") + .run( + context -> + assertThat(context).getBean("openTelemetry").isInstanceOf(OpenTelemetrySdk.class)); + } + + @Test + void shouldInitializeNoopOpenTelemetryWhenSdkIsDisabled() { + this.contextRunner + .withPropertyValues( + "otel.file_format=1.0-rc.1", + "otel.disabled=true", + "otel.resource.attributes=service.name=workflow-backend-dev,service.version=3c8f9ce9") + .run( + context -> + assertThat(context).getBean("openTelemetry").isEqualTo(OpenTelemetry.noop())); + } + + @Test + void shouldLoadInstrumentation() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(SpringWebInstrumentationAutoConfiguration.class)) + .withPropertyValues("otel.file_format=1.0-rc.1") + .run(context -> assertThat(context).hasBean("otelRestTemplateBeanPostProcessor")); + } + + @Test + void shouldNotLoadInstrumentationWhenDefaultIsDisabled() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(SpringWebInstrumentationAutoConfiguration.class)) + .withPropertyValues( + "otel.file_format=1.0-rc.1", + "otel.instrumentation/development.java.spring_starter.instrumentation_mode=none") + .run(context -> assertThat(context).doesNotHaveBean("otelRestTemplateBeanPostProcessor")); + } + + @Test + void shouldLoadInstrumentationWhenExplicitlyEnabled() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(SpringWebInstrumentationAutoConfiguration.class)) + .withPropertyValues( + "otel.file_format=1.0-rc.1", + "otel.instrumentation/development.java.spring_starter.instrumentation_mode=none", + "otel.instrumentation/development.java.spring_web.enabled=true") + .run(context -> assertThat(context).hasBean("otelRestTemplateBeanPostProcessor")); + } + + @Test + void shouldNotLoadInstrumentationWhenExplicitlyDisabled() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(SpringWebInstrumentationAutoConfiguration.class)) + .withPropertyValues( + "otel.file_format=1.0-rc.1", + "otel.instrumentation/development.java.spring_starter.instrumentation_mode=none", + "otel.instrumentation/development.java.spring_web.enabled=false") + .run(context -> assertThat(context).doesNotHaveBean("otelRestTemplateBeanPostProcessor")); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/testDeclarativeConfig/resources/application.yaml b/instrumentation/spring/spring-boot-autoconfigure/src/testDeclarativeConfig/resources/application.yaml new file mode 100644 index 000000000000..8f263bc8ff50 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/testDeclarativeConfig/resources/application.yaml @@ -0,0 +1,10 @@ +otel: + # "file_format" serves as opt-in to the new file format + file_format: "1.0-rc.1" + + # very lightweight test to make sure the declarative config is loaded + # the full config is tested in smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig + instrumentation/development: + java: + foo: + bar: baz diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfigurationTest.java index bae340b948ef..3e16f15b23ef 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/web/RestClientInstrumentationAutoConfigurationTest.java @@ -8,7 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -22,8 +23,10 @@ class RestClientInstrumentationAutoConfigurationTest { new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withBean(RestClient.class, RestClient::create) .withConfiguration( AutoConfigurations.of(RestClientInstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java index d064262095c6..a96d6abc2f4e 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/testSpring3/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java @@ -9,7 +9,8 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import jakarta.servlet.Filter; import java.util.Collections; @@ -24,8 +25,10 @@ class SpringWebMvcInstrumentation6AutoConfigurationTest { new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) .withBean( - ConfigProperties.class, - () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) + InstrumentationConfig.class, + () -> + new ConfigPropertiesBridge( + DefaultConfigProperties.createFromMap(Collections.emptyMap()))) .withConfiguration( AutoConfigurations.of(SpringWebMvc6InstrumentationAutoConfiguration.class)); diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SpanLoggingCustomizerProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SpanLoggingCustomizerProvider.java index 4a012d253d46..00634e858503 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SpanLoggingCustomizerProvider.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/SpanLoggingCustomizerProvider.java @@ -7,69 +7,18 @@ import com.google.auto.service.AutoService; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.instrumentation.logging.internal.AbstractSpanLoggingCustomizerProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ConsoleExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleSpanProcessorModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel; /** Adds span logging exporter for debug mode */ @AutoService(DeclarativeConfigurationCustomizerProvider.class) -public class SpanLoggingCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { +public class SpanLoggingCustomizerProvider extends AbstractSpanLoggingCustomizerProvider { @Override - public void customize(DeclarativeConfigurationCustomizer customizer) { - customizer.addModelCustomizer( - model -> { - maybeEnableLoggingExporter(model); - return model; - }); - } - - private static void maybeEnableLoggingExporter(OpenTelemetryConfigurationModel model) { + protected boolean isEnabled(OpenTelemetryConfigurationModel model) { // read from system properties as it's an early init property and the config bridge is not // available here - if (!ConfigPropertiesUtil.getBoolean("otel.javaagent.debug", false)) { - return; - } - // don't install another instance if the user has already explicitly requested it. - if (loggingExporterIsAlreadyConfigured(model)) { - return; - } - TracerProviderModel tracerProvider = model.getTracerProvider(); - if (tracerProvider == null) { - tracerProvider = new TracerProviderModel(); - model.withTracerProvider(tracerProvider); - } - SpanProcessorModel processor = - new SpanProcessorModel() - .withSimple( - new SimpleSpanProcessorModel() - .withExporter(new SpanExporterModel().withConsole(new ConsoleExporterModel()))); - tracerProvider.getProcessors().add(processor); - } - - private static boolean loggingExporterIsAlreadyConfigured(OpenTelemetryConfigurationModel model) { - TracerProviderModel tracerProvider = model.getTracerProvider(); - if (tracerProvider == null) { - return false; - } - for (SpanProcessorModel processor : tracerProvider.getProcessors()) { - SimpleSpanProcessorModel simple = processor.getSimple(); - if (simple == null) { - continue; - } - SpanExporterModel exporter = simple.getExporter(); - if (exporter == null) { - continue; - } - if (exporter.getConsole() != null) { - return true; - } - } - return false; + return ConfigPropertiesUtil.getBoolean("otel.javaagent.debug", false); } } diff --git a/sdk-autoconfigure-support/build.gradle.kts b/sdk-autoconfigure-support/build.gradle.kts index c2948406f7e1..cf9be9f90a7d 100644 --- a/sdk-autoconfigure-support/build.gradle.kts +++ b/sdk-autoconfigure-support/build.gradle.kts @@ -7,6 +7,7 @@ group = "io.opentelemetry.instrumentation" dependencies { api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + api("io.opentelemetry:opentelemetry-sdk-extension-incubator") compileOnly("com.google.code.findbugs:annotations") testCompileOnly("com.google.code.findbugs:annotations") diff --git a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/logging/internal/AbstractSpanLoggingCustomizerProvider.java b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/logging/internal/AbstractSpanLoggingCustomizerProvider.java new file mode 100644 index 000000000000..17c15209ca4c --- /dev/null +++ b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/logging/internal/AbstractSpanLoggingCustomizerProvider.java @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.logging.internal; + +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.ConsoleExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleSpanProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel; + +/** + * Adds span logging exporter for debug mode + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public abstract class AbstractSpanLoggingCustomizerProvider + implements DeclarativeConfigurationCustomizerProvider { + + protected abstract boolean isEnabled(OpenTelemetryConfigurationModel model); + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + maybeEnableLoggingExporter(model); + return model; + }); + } + + private void maybeEnableLoggingExporter(OpenTelemetryConfigurationModel model) { + if (!isEnabled(model)) { + return; + } + // don't install another instance if the user has already explicitly requested it. + if (loggingExporterIsAlreadyConfigured(model)) { + return; + } + TracerProviderModel tracerProvider = model.getTracerProvider(); + if (tracerProvider == null) { + tracerProvider = new TracerProviderModel(); + model.withTracerProvider(tracerProvider); + } + SpanProcessorModel processor = + new SpanProcessorModel() + .withSimple( + new SimpleSpanProcessorModel() + .withExporter(new SpanExporterModel().withConsole(new ConsoleExporterModel()))); + tracerProvider.getProcessors().add(processor); + } + + private static boolean loggingExporterIsAlreadyConfigured(OpenTelemetryConfigurationModel model) { + TracerProviderModel tracerProvider = model.getTracerProvider(); + if (tracerProvider == null) { + return false; + } + for (SpanProcessorModel processor : tracerProvider.getProcessors()) { + SimpleSpanProcessorModel simple = processor.getSimple(); + if (simple == null) { + continue; + } + SpanExporterModel exporter = simple.getExporter(); + if (exporter == null) { + continue; + } + if (exporter.getConsole() != null) { + return true; + } + } + return false; + } +} diff --git a/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts b/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts index 788a8042bd30..678510b3e43d 100644 --- a/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts +++ b/smoke-tests-otel-starter/spring-boot-2/build.gradle.kts @@ -30,3 +30,22 @@ configurations.configureEach { force("org.slf4j:slf4j-api:1.7.36") } } + +testing { + suites { + val testDeclarativeConfig by registering(JvmTestSuite::class) { + dependencies { + implementation(project()) + implementation(project(":smoke-tests-otel-starter:spring-boot-common")) + implementation("org.springframework.boot:spring-boot-starter-web:2.6.15") + implementation("org.springframework.boot:spring-boot-starter-test:2.6.15") + } + } + } +} + +tasks { + check { + dependsOn(testing.suites) + } +} diff --git a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java new file mode 100644 index 000000000000..214e464fde8b --- /dev/null +++ b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.spring.smoketest; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.ServiceAttributes; +import io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes; +import io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes; +import org.assertj.core.api.AbstractCharSequenceAssert; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.client.RestTemplateBuilder; + +@SpringBootTest( + classes = { + OtelSpringStarterSmokeTestApplication.class, + AbstractOtelSpringStarterSmokeTest.TestConfiguration.class, + SpringSmokeOtelConfiguration.class + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) +class OtelSpringStarterSmokeTest extends AbstractSpringStarterSmokeTest { + + @Autowired private RestTemplateBuilder restTemplateBuilder; + + // can't use @LocalServerPort annotation since it moved packages between Spring Boot 2 and 3 + @Value("${local.server.port}") + private int port; + + @Test + void restTemplate() { + testing.clearAllExportedData(); + + org.springframework.web.client.RestTemplate restTemplate = + restTemplateBuilder.rootUri("http://localhost:" + port).build(); + restTemplate.getForObject(OtelSpringStarterSmokeTestController.PING, String.class); + testing.waitAndAssertTraces( + traceAssert -> + traceAssert.hasSpansSatisfyingExactly( + span -> + HttpSpanDataAssert.create(span) + .assertClientGetRequest("/ping") + .hasResourceSatisfying( + r -> + r.hasAttribute( + // to make sure the declarative config is picked up + // in application.yaml + ServiceAttributes.SERVICE_NAME, + "declarative-config-spring-boot-2")), + span -> + span.hasKind(SpanKind.SERVER) + .hasResourceSatisfying( + r -> + r.hasAttribute( + TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME, + "opentelemetry-spring-boot-starter") + .hasAttribute( + satisfies( + TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION, + AbstractCharSequenceAssert::isNotBlank)) + .hasAttribute( + satisfies( + ServiceIncubatingAttributes.SERVICE_INSTANCE_ID, + AbstractCharSequenceAssert::isNotBlank))) + .hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping"), + AbstractSpringStarterSmokeTest::withSpanAssert)); + } +} diff --git a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml new file mode 100644 index 000000000000..6b24c0ee67fa --- /dev/null +++ b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml @@ -0,0 +1,47 @@ +otel: + # "file_format" serves as opt-in to the new file format + file_format: "1.0-rc.1" + resource: + detection/development: + detectors: + - service: + attributes: + - name: service.name + value: declarative-config-spring-boot-2 + + tracer_provider: + processors: + - simple: + exporter: + test: + - simple: + exporter: + console: + + logger_provider: + processors: + - simple: + exporter: + test: + + meter_provider: + readers: + - periodic: + # Set really long interval. We'll call forceFlush when we need the metrics + # instead of collecting them periodically. + interval: 1000000 + exporter: + test: + + propagator: + composite: + - tracecontext: + - baggage: + + instrumentation/development: + java: + runtime-telemetry: + emit_experimental_telemetry: true + http: + client: + emit_experimental_telemetry: true diff --git a/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/SpringSmokeTestRunner.java b/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/SpringSmokeTestRunner.java index 471016ee883e..0675f3b0b61a 100644 --- a/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/SpringSmokeTestRunner.java +++ b/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/SpringSmokeTestRunner.java @@ -7,6 +7,9 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner; +import io.opentelemetry.instrumentation.testing.provider.TestLogRecordExporterComponentProvider; +import io.opentelemetry.instrumentation.testing.provider.TestMetricExporterComponentProvider; +import io.opentelemetry.instrumentation.testing.provider.TestSpanExporterComponentProvider; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; @@ -36,6 +39,11 @@ static void resetExporters() { testSpanExporter = InMemorySpanExporter.create(); testMetricExporter = InMemoryMetricExporter.create(AggregationTemporality.DELTA); testLogRecordExporter = InMemoryLogRecordExporter.create(); + + // for declarative config + TestLogRecordExporterComponentProvider.setLogRecordExporter(testLogRecordExporter); + TestMetricExporterComponentProvider.setMetricExporter(testMetricExporter); + TestSpanExporterComponentProvider.setSpanExporter(testSpanExporter); } @Override