diff --git a/instrumentation-api-incubator/README.md b/instrumentation-api-incubator/README.md new file mode 100644 index 000000000000..348cd6bb1720 --- /dev/null +++ b/instrumentation-api-incubator/README.md @@ -0,0 +1,91 @@ +# OpenTelemetry Instrumentation API Incubator + +Instrumentation API Incubator is a collection of libraries that provide additional functionality +for OpenTelemetry instrumentation and auto-configuration. It is intended to be used by +instrumentation authors and auto-configuration providers to enhance their capabilities and provide a +more consistent experience when working with OpenTelemetry. + +## Declarative Config Bridge + +Declarative Config Bridge allows instrumentation authors to access configuration in a uniform way, +regardless of the configuration source. + +The bridge allows you to read configuration using the system property style when dealing with +declarative configuration. + +### Example + +As an example, let's look at the inferred spans configuration. +First, there is a configuration method that reads the properties and is unaware of the source of the +configuration: + +```java +class InferredSpansConfig { + static SpanProcessor create(ConfigProperties properties) { + // read properties here + boolean backupDiagnosticFiles = + properties.getBoolean("otel.inferred.spans.backup.diagnostic.files", false); + } +} +``` + +The auto configuration **without declarative config** passes the provided properties directly: + +```java + +@AutoService(AutoConfigurationCustomizerProvider.class) +public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvider { + + @Override + public void customize(AutoConfigurationCustomizer config) { + config.addTracerProviderCustomizer( + (providerBuilder, properties) -> { + providerBuilder.addSpanProcessor(InferredSpansConfig.create(properties)); + return providerBuilder; + }); + } +} +``` + +The auto configuration **with declarative config** uses the Declarative Config Bridge to be able to +use common configuration method: + +Let's first look at the yaml file that is used to configure the inferred spans processor: + +```yaml +file_format: 1.0-rc.1 +tracer_provider: + processors: + - inferred_spans: + backup: + diagnostic: + files: true +``` + +And now the component provider that uses the Declarative Config Bridge: + +```java + +@AutoService(ComponentProvider.class) +public class InferredSpansComponentProvider implements ComponentProvider { + + @Override + public String getName() { + return "inferred_spans"; + } + + @Override + public SpanProcessor create(DeclarativeConfigProperties config) { + return InferredSpansConfig.create( + new DeclarativeConfigPropertiesBridgeBuilder() + // crop the prefix, because the properties are under the "inferred_spans" processor + .addMapping("otel.inferred.spans.", "") + .build(config)); + } + + @Override + public Class getType() { + return SpanProcessor.class; + } +} +``` diff --git a/instrumentation-api-incubator/build.gradle.kts b/instrumentation-api-incubator/build.gradle.kts index 707fd2217c1b..c6e0eaa576ba 100644 --- a/instrumentation-api-incubator/build.gradle.kts +++ b/instrumentation-api-incubator/build.gradle.kts @@ -14,6 +14,8 @@ dependencies { api("io.opentelemetry.semconv:opentelemetry-semconv") api(project(":instrumentation-api")) api("io.opentelemetry:opentelemetry-api-incubator") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") compileOnly("com.google.auto.value:auto-value-annotations") annotationProcessor("com.google.auto.value:auto-value") @@ -22,6 +24,8 @@ dependencies { testImplementation("io.opentelemetry:opentelemetry-sdk") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") } tasks { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/ConfigPropertiesUtil.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/ConfigPropertiesUtil.java new file mode 100644 index 000000000000..6a026e38d468 --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/ConfigPropertiesUtil.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge; + +public final class ConfigPropertiesUtil { + private ConfigPropertiesUtil() {} + + public static String propertyYamlPath(String propertyName) { + return yamlPath(propertyName); + } + + static String yamlPath(String property) { + String[] segments = DeclarativeConfigPropertiesBridge.getSegments(property); + if (segments.length == 0) { + throw new IllegalArgumentException("Invalid property: " + property); + } + + return "'instrumentation/development' / 'java' / '" + String.join("' / '", segments) + "'"; + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridge.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java similarity index 97% rename from javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridge.java rename to instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java index e2e782443361..000d928159d4 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridge.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridge.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge; import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; @@ -169,7 +169,7 @@ private T getPropertyValue( return extractor.apply(target, lastPart); } - private static String[] getSegments(String property) { + static String[] getSegments(String property) { if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) { property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length()); } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilder.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java similarity index 86% rename from javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilder.java rename to instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java index 05da62862dec..e3a30c196fd5 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilder.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge; import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; @@ -21,9 +21,6 @@ /** * A builder for {@link DeclarativeConfigPropertiesBridge} that allows adding translations and fixed * values for properties. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. */ public class DeclarativeConfigPropertiesBridgeBuilder { /** @@ -82,6 +79,17 @@ public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenT "AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java"); } + /** + * Build {@link ConfigProperties} from the provided {@link DeclarativeConfigProperties} node. + * + * @param node the declarative config properties to build from + * @return a new instance of {@link ConfigProperties} + */ + public ConfigProperties build(@Nullable DeclarativeConfigProperties node) { + return new DeclarativeConfigPropertiesBridge( + node == null ? empty() : node, mappings, overrideValues); + } + /** * Build {@link ConfigProperties} from the {@link DeclarativeConfigProperties} provided by the * instrumentation configuration. @@ -94,12 +102,7 @@ public ConfigProperties build(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenT */ public ConfigProperties buildFromInstrumentationConfig( @Nullable DeclarativeConfigProperties instrumentationConfig) { - // leave the name "build" for a future method that builds from a DeclarativeConfigProperties - // instance that doesn't come from the top-level instrumentation config - if (instrumentationConfig == null) { - instrumentationConfig = DeclarativeConfigProperties.empty(); - } - return new DeclarativeConfigPropertiesBridge( - instrumentationConfig.getStructured("java", empty()), mappings, overrideValues); + return build( + instrumentationConfig == null ? null : instrumentationConfig.getStructured("java")); } } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/ConfigPropertiesUtilTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/ConfigPropertiesUtilTest.java new file mode 100644 index 000000000000..535fda407160 --- /dev/null +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/ConfigPropertiesUtilTest.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ConfigPropertiesUtilTest { + @Test + void propertyYamlPath() { + assertThat(ConfigPropertiesUtil.propertyYamlPath("google.otel.auth.target.signals")) + .isEqualTo( + "'instrumentation/development' / 'java' / 'google' / 'otel' / 'auth' / 'target' / 'signals'"); + } +} diff --git a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilderTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilderTest.java similarity index 94% rename from javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilderTest.java rename to instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilderTest.java index dd41958eebcb..96b4ff0f3b9f 100644 --- a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeBuilderTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeBuilderTest.java @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -47,7 +46,7 @@ void shouldUseConfigProviderForDeclarativeConfiguration() { when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue); DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class); - when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock); + when(instrumentationConfigMock.getStructured(eq("java"))).thenReturn(javaNodeMock); ConfigProvider configProviderMock = mock(ConfigProvider.class); when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock); diff --git a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeTest.java similarity index 98% rename from javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeTest.java rename to instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeTest.java index deeb424b8abe..879c2223646a 100644 --- a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/internal/DeclarativeConfigPropertiesBridgeTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/sdk/config/bridge/DeclarativeConfigPropertiesBridgeTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.extension.internal; +package io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge; import static org.assertj.core.api.Assertions.assertThat; diff --git a/javaagent-extension-api/src/test/resources/config.yaml b/instrumentation-api-incubator/src/test/resources/config.yaml similarity index 95% rename from javaagent-extension-api/src/test/resources/config.yaml rename to instrumentation-api-incubator/src/test/resources/config.yaml index e0f2020bda37..5f8bbd1943b0 100644 --- a/javaagent-extension-api/src/test/resources/config.yaml +++ b/instrumentation-api-incubator/src/test/resources/config.yaml @@ -1,4 +1,4 @@ -file_format: 0.4 +file_format: 1.0-rc.1 instrumentation/development: java: acme: diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index c44a7c4c0a65..0d94d1baeb09 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry:opentelemetry-sdk") + implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") implementation("io.opentelemetry:opentelemetry-extension-kotlin") implementation("io.opentelemetry:opentelemetry-extension-trace-propagators") // the incubator's ViewConfigCustomizer is used to support loading yaml-based metric views diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java index 29c08e7dbe75..3a31d4986cdc 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java @@ -6,8 +6,8 @@ package io.opentelemetry.javaagent.tooling; import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.instrumentation.api.incubator.sdk.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.javaagent.bootstrap.OpenTelemetrySdkAccess; -import io.opentelemetry.javaagent.extension.internal.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; diff --git a/javaagent/build.gradle.kts b/javaagent/build.gradle.kts index c91211d935f9..f4eec724b26c 100644 --- a/javaagent/build.gradle.kts +++ b/javaagent/build.gradle.kts @@ -57,7 +57,6 @@ dependencies { bootstrapLibs(project(":instrumentation-api")) // opentelemetry-api is an api dependency of :instrumentation-api, but opentelemetry-api-incubator is not bootstrapLibs("io.opentelemetry:opentelemetry-api-incubator") - bootstrapLibs(project(":instrumentation-api-incubator")) bootstrapLibs(project(":instrumentation-annotations-support")) bootstrapLibs(project(":javaagent-bootstrap")) @@ -71,8 +70,11 @@ dependencies { exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure-spi") } baseJavaagentLibs(project(":javaagent-extension-api")) + baseJavaagentLibs(project(":instrumentation-api-incubator")) - baseJavaagentLibs(project(":javaagent-tooling")) + baseJavaagentLibs(project(":javaagent-tooling")) { + exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure-spi") + } baseJavaagentLibs(project(":javaagent-internal-logging-application")) baseJavaagentLibs(project(":javaagent-internal-logging-simple", configuration = "shadow")) baseJavaagentLibs(project(":muzzle")) @@ -147,8 +149,7 @@ tasks { val buildBootstrapLibs by registering(ShadowJar::class) { configurations = listOf(bootstrapLibs) - // exclude the agent part of the javaagent-extension-api; these classes will be added in relocate tasks - exclude("io/opentelemetry/javaagent/extension/**") + excludeNonBootstrapClasses() duplicatesStrategy = DuplicatesStrategy.EXCLUDE @@ -286,7 +287,8 @@ tasks { doLast { val filePath = rootDir.toPath().resolve("licenses").resolve("licenses.md") if (Files.exists(filePath)) { - val datePattern = Pattern.compile("^_[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} .*_$") + val datePattern = + Pattern.compile("^_[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} .*_$") val lines = Files.readAllLines(filePath) // 4th line contains the timestamp of when the license report was generated, replace it with // an empty line @@ -412,7 +414,8 @@ fun CopySpec.copyByteBuddy(jar: Provider) { eachFile { if (path.startsWith("net/bytebuddy/") && // this is our class that we have placed in the byte buddy package, need to preserve it - !path.startsWith("net/bytebuddy/agent/builder/AgentBuilderUtil")) { + !path.startsWith("net/bytebuddy/agent/builder/AgentBuilderUtil") + ) { exclude() } else if (path.startsWith("META-INF/versions/9/net/bytebuddy/")) { path = path.removePrefix("META-INF/versions/9/") @@ -422,17 +425,30 @@ fun CopySpec.copyByteBuddy(jar: Provider) { } } +// exclude bootstrap projects from javaagent libs - they won't be added to inst/ +fun ShadowJar.excludeNonBootstrapClasses() { + // exclude the agent part of the javaagent-extension-api; these classes will be added in relocate tasks + exclude("io/opentelemetry/javaagent/extension/**") + exclude("**/instrumentation/api/incubator/sdk/**") +} + // exclude bootstrap projects from javaagent libs - they won't be added to inst/ fun ShadowJar.excludeBootstrapClasses() { dependencies { exclude(project(":instrumentation-api")) - exclude(project(":instrumentation-api-incubator")) exclude(project(":instrumentation-annotations-support")) exclude(project(":javaagent-bootstrap")) } // exclude the bootstrap part of the javaagent-extension-api exclude("io/opentelemetry/javaagent/bootstrap/**") + + // all in instrumentation-api-incubator except the bridge package + exclude("io/opentelemetry/instrumentation/api/incubator/builder/**") + exclude("io/opentelemetry/instrumentation/api/incubator/config/**") + exclude("io/opentelemetry/instrumentation/api/incubator/instrumenter/**") + exclude("io/opentelemetry/instrumentation/api/incubator/log/**") + exclude("io/opentelemetry/instrumentation/api/incubator/semconv/**") } class JavaagentProvider( diff --git a/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java b/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java index c83a2161a53a..fa0a44bc5dd5 100644 --- a/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java +++ b/testing-common/integration-tests/src/test/java/instrumentation/AgentInstrumentationTest.java @@ -50,10 +50,15 @@ void classPathSetUp() throws ClassNotFoundException { for (ClassPath.ClassInfo info : getTestClasspath().getAllClasses()) { for (String bootstrapPrefix : BOOTSTRAP_PACKAGE_PREFIXES) { if (info.getName().startsWith(bootstrapPrefix)) { - Class bootstrapClass = Class.forName(info.getName()); - ClassLoader loader = bootstrapClass.getClassLoader(); - if (loader != BOOTSTRAP_CLASSLOADER) { - bootstrapClassesIncorrectlyLoaded.add(bootstrapClass); + try { + Class bootstrapClass = Class.forName(info.getName()); + ClassLoader loader = bootstrapClass.getClassLoader(); + if (loader != BOOTSTRAP_CLASSLOADER) { + bootstrapClassesIncorrectlyLoaded.add(bootstrapClass); + } + } catch (ClassNotFoundException | NoClassDefFoundError e) { + throw new RuntimeException( + "Failed to load bootstrap class: " + info.getName() + " in " + bootstrapPrefix, e); } } }