make declarative config bridge usable by spring starter and contrib (#14497)
Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com>
This commit is contained in:
parent
99b89ee108
commit
136efcb48e
|
|
@ -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<SpanProcessor> {
|
||||
|
||||
@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<SpanProcessor> getType() {
|
||||
return SpanProcessor.class;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) + "'";
|
||||
}
|
||||
}
|
||||
|
|
@ -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 @@ final class DeclarativeConfigPropertiesBridge implements ConfigProperties {
|
|||
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());
|
||||
}
|
||||
|
|
@ -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 @@ import javax.annotation.Nullable;
|
|||
/**
|
||||
* A builder for {@link DeclarativeConfigPropertiesBridge} that allows adding translations and fixed
|
||||
* values for properties.
|
||||
*
|
||||
* <p>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 class DeclarativeConfigPropertiesBridgeBuilder {
|
|||
"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 class DeclarativeConfigPropertiesBridgeBuilder {
|
|||
*/
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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'");
|
||||
}
|
||||
}
|
||||
|
|
@ -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 @@ class DeclarativeConfigPropertiesBridgeBuilderTest {
|
|||
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);
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
file_format: 0.4
|
||||
file_format: 1.0-rc.1
|
||||
instrumentation/development:
|
||||
java:
|
||||
acme:
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<RegularFile>) {
|
|||
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<RegularFile>) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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(
|
||||
|
|
|
|||
|
|
@ -50,10 +50,15 @@ class AgentInstrumentationTest {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue