diff --git a/instrumentation/resources/library/build.gradle.kts b/instrumentation/resources/library/build.gradle.kts index 931468c7d94c..bb41a8b31dd9 100644 --- a/instrumentation/resources/library/build.gradle.kts +++ b/instrumentation/resources/library/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-api-incubator") implementation("io.opentelemetry:opentelemetry-sdk-common") implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") implementation("io.opentelemetry.semconv:opentelemetry-semconv") annotationProcessor("com.google.auto.service:auto-service") diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java index 594b69cad2cb..31f4d18c2ec5 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java @@ -45,7 +45,6 @@ public AttributeBuilder add(AttributeKey key, Function> ge } private Set> filteredKeys; - private final Map, Function>> attributeGetters = new HashMap<>(); diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java index 7ffaeaa2d170..b661d12917ae 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetector.java @@ -5,57 +5,25 @@ package io.opentelemetry.instrumentation.resources; -import static java.util.logging.Level.FINE; - import com.google.auto.service.AutoService; -import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.resources.internal.JarServiceNameResourceExtractor; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ServiceAttributes; -import java.nio.file.Path; import java.util.Map; -import java.util.Optional; -import java.util.function.Supplier; -import java.util.logging.Logger; /** - * A {@link ResourceProvider} that will attempt to detect the application name from the jar name. + * A {@link ResourceProvider} that will attempt to detect the service.name from the + * main jar file name. */ @AutoService(ResourceProvider.class) public final class JarServiceNameDetector implements ConditionalResourceProvider { - private static final Logger logger = Logger.getLogger(JarServiceNameDetector.class.getName()); - - private final Supplier> jarPathSupplier; - - @SuppressWarnings("unused") // SPI - public JarServiceNameDetector() { - this(MainJarPathHolder::getJarPath); - } - - private JarServiceNameDetector(Supplier> jarPathSupplier) { - this.jarPathSupplier = jarPathSupplier; - } - - // visible for tests - JarServiceNameDetector(MainJarPathFinder jarPathFinder) { - this(() -> Optional.ofNullable(jarPathFinder.detectJarPath())); - } - @Override public Resource createResource(ConfigProperties config) { - return jarPathSupplier - .get() - .map( - jarPath -> { - String serviceName = getServiceName(jarPath); - logger.log( - FINE, "Auto-detected service name from the jar file name: {0}", serviceName); - return Resource.create(Attributes.of(ServiceAttributes.SERVICE_NAME, serviceName)); - }) - .orElseGet(Resource::empty); + return new JarServiceNameResourceExtractor().extract(); } @Override @@ -67,12 +35,6 @@ public boolean shouldApply(ConfigProperties config, Resource existing) { && "unknown_service:java".equals(existing.getAttribute(ServiceAttributes.SERVICE_NAME)); } - private static String getServiceName(Path jarPath) { - String jarName = jarPath.getFileName().toString(); - int dotIndex = jarName.lastIndexOf("."); - return dotIndex == -1 ? jarName : jarName.substring(0, dotIndex); - } - @Override public int order() { // make it run later than the SpringBootServiceNameDetector diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java index 2946ad3d8776..927f85e8fe60 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java @@ -5,79 +5,48 @@ package io.opentelemetry.instrumentation.resources; -import static java.util.logging.Level.FINE; - import com.google.auto.service.AutoService; +import io.opentelemetry.instrumentation.resources.internal.ManifestResourceExtractor; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ServiceAttributes; -import java.io.IOException; -import java.nio.file.Path; import java.util.Optional; -import java.util.function.Function; import java.util.function.Supplier; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.logging.Logger; /** * A {@link ResourceProvider} that will attempt to detect the service.name and * service.version from META-INF/MANIFEST.MF. */ @AutoService(ResourceProvider.class) -public final class ManifestResourceProvider extends AttributeResourceProvider { - - private static final Logger logger = Logger.getLogger(ManifestResourceProvider.class.getName()); +public final class ManifestResourceProvider extends AttributeResourceProvider { @SuppressWarnings("unused") // SPI public ManifestResourceProvider() { - this(MainJarPathHolder::getJarPath, ManifestResourceProvider::readManifest); + this(() -> new ManifestResourceExtractor().extract()); } - private ManifestResourceProvider( - Supplier> jarPathSupplier, Function> manifestReader) { + // Visible for testing + ManifestResourceProvider(Supplier resourceSupplier) { super( - new AttributeProvider() { + new AttributeProvider() { @Override - public Optional readData() { - return jarPathSupplier.get().flatMap(manifestReader); + public Optional readData() { + return Optional.of(resourceSupplier.get()); } @Override - public void registerAttributes(Builder builder) { + public void registerAttributes(Builder builder) { builder .add( ServiceAttributes.SERVICE_NAME, - manifest -> { - String serviceName = - manifest.getMainAttributes().getValue("Implementation-Title"); - return Optional.ofNullable(serviceName); - }) + r -> Optional.ofNullable(r.getAttribute(ServiceAttributes.SERVICE_NAME))) .add( ServiceAttributes.SERVICE_VERSION, - manifest -> { - String serviceVersion = - manifest.getMainAttributes().getValue("Implementation-Version"); - return Optional.ofNullable(serviceVersion); - }); + r -> Optional.ofNullable(r.getAttribute(ServiceAttributes.SERVICE_VERSION))); } }); } - // Visible for testing - ManifestResourceProvider( - MainJarPathFinder jarPathFinder, Function> manifestReader) { - this(() -> Optional.ofNullable(jarPathFinder.detectJarPath()), manifestReader); - } - - private static Optional readManifest(Path jarPath) { - try (JarFile jarFile = new JarFile(jarPath.toFile(), false)) { - return Optional.of(jarFile.getManifest()); - } catch (IOException exception) { - logger.log(FINE, "Error reading manifest", exception); - return Optional.empty(); - } - } - @Override public int order() { // make it run later than SpringBootServiceNameDetector diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java deleted file mode 100644 index c835f876965c..000000000000 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.resources; - -final class ProcessArguments { - - static String[] getProcessArguments() { - return new String[0]; - } - - private ProcessArguments() {} -} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java index 666c2964d8e2..20470efbfa92 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.resources.internal.ProcessArguments; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.SchemaUrls; import java.io.File; diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java index 426f3c2ec9b4..141e4bd91386 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ContainerResourceComponentProvider.java @@ -19,6 +19,6 @@ @AutoService(ComponentProvider.class) public class ContainerResourceComponentProvider extends ResourceComponentProvider { public ContainerResourceComponentProvider() { - super("container", ContainerResource::get); + super("container", p -> ContainerResource.get()); } } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java index dd0b0f519cfd..a65382557539 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/HostResourceComponentProvider.java @@ -21,6 +21,6 @@ @AutoService(ComponentProvider.class) public class HostResourceComponentProvider extends ResourceComponentProvider { public HostResourceComponentProvider() { - super("host", () -> HostResource.get().merge(HostIdResource.get()).merge(OsResource.get())); + super("host", p -> HostResource.get().merge(HostIdResource.get()).merge(OsResource.get())); } } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarResourceComponentProvider.java new file mode 100644 index 000000000000..938dae452199 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarResourceComponentProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; + +/** + * Declarative config jar resource provider. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class JarResourceComponentProvider extends ResourceComponentProvider { + public JarResourceComponentProvider() { + super("jar", p -> new JarServiceNameResourceExtractor().extract()); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractor.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractor.java new file mode 100644 index 000000000000..d9c09b9a4073 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractor.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import static java.util.logging.Level.FINE; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ServiceAttributes; +import java.nio.file.Path; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Logger; + +/** + * A resource extractor that will attempt to detect the service.name from the main jar + * file name. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class JarServiceNameResourceExtractor { + + private static final Logger logger = + Logger.getLogger(JarServiceNameResourceExtractor.class.getName()); + + private final Supplier> jarPathSupplier; + + public JarServiceNameResourceExtractor() { + this(MainJarPathHolder::getJarPath); + } + + // visible for tests + JarServiceNameResourceExtractor(MainJarPathFinder jarPathFinder) { + this(() -> Optional.ofNullable(jarPathFinder.detectJarPath())); + } + + private JarServiceNameResourceExtractor(Supplier> jarPathSupplier) { + this.jarPathSupplier = jarPathSupplier; + } + + public Resource extract() { + return jarPathSupplier + .get() + .map( + jarPath -> { + String serviceName = getServiceName(jarPath); + logger.log( + FINE, "Auto-detected service name from the jar file name: {0}", serviceName); + return Resource.create(Attributes.of(ServiceAttributes.SERVICE_NAME, serviceName)); + }) + .orElseGet(Resource::empty); + } + + private static String getServiceName(Path jarPath) { + String jarName = jarPath.getFileName().toString(); + int dotIndex = jarName.lastIndexOf("."); + return dotIndex == -1 ? jarName : jarName.substring(0, dotIndex); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathFinder.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathFinder.java similarity index 97% rename from instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathFinder.java rename to instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathFinder.java index c0c7588698f2..dec6f642deb4 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathFinder.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathFinder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import java.nio.file.Files; import java.nio.file.InvalidPathException; diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathHolder.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathHolder.java similarity index 86% rename from instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathHolder.java rename to instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathHolder.java index c6948d3eca39..ee6df00ca345 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/MainJarPathHolder.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/MainJarPathHolder.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import java.nio.file.Path; import java.util.Optional; diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceComponentProvider.java new file mode 100644 index 000000000000..0d290adef0a2 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceComponentProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; + +/** + * Declarative config manifest resource provider. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class ManifestResourceComponentProvider extends ResourceComponentProvider { + public ManifestResourceComponentProvider() { + super("manifest", p -> new ManifestResourceExtractor().extract()); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractor.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractor.java new file mode 100644 index 000000000000..2fede180f443 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractor.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import static java.util.logging.Level.FINE; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ServiceAttributes; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +/** + * A resource extractor that will attempt to detect the service.name and + * service.version from META-INF/MANIFEST.MF. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ManifestResourceExtractor { + + private static final Logger logger = Logger.getLogger(ManifestResourceExtractor.class.getName()); + + private final Supplier> jarPathSupplier; + + private final Function> manifestReader; + + public ManifestResourceExtractor() { + this(MainJarPathHolder::getJarPath, ManifestResourceExtractor::readManifest); + } + + // Visible for testing + ManifestResourceExtractor( + MainJarPathFinder jarPathFinder, Function> manifestReader) { + this(() -> Optional.ofNullable(jarPathFinder.detectJarPath()), manifestReader); + } + + private ManifestResourceExtractor( + Supplier> jarPathSupplier, Function> manifestReader) { + this.jarPathSupplier = jarPathSupplier; + this.manifestReader = manifestReader; + } + + public Resource extract() { + return jarPathSupplier + .get() + .flatMap(manifestReader) + .map(manifest -> extract(manifest)) + .orElseGet(Resource::empty); + } + + private static Resource extract(Manifest manifest) { + String serviceName = manifest.getMainAttributes().getValue("Implementation-Title"); + AttributesBuilder builder = Attributes.builder(); + if (serviceName != null) { + builder.put(ServiceAttributes.SERVICE_NAME, serviceName); + } + + String serviceVersion = manifest.getMainAttributes().getValue("Implementation-Version"); + if (serviceVersion != null) { + builder.put(ServiceAttributes.SERVICE_VERSION, serviceVersion); + } + return Resource.create(builder.build()); + } + + private static Optional readManifest(Path jarPath) { + try (JarFile jarFile = new JarFile(jarPath.toFile(), false)) { + return Optional.of(jarFile.getManifest()); + } catch (IOException exception) { + logger.log(FINE, "Error reading manifest", exception); + return Optional.empty(); + } + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java new file mode 100644 index 000000000000..24ebce1fc1cb --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class ProcessArguments { + + public static String[] getProcessArguments() { + return new String[0]; + } + + private ProcessArguments() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java index cbdb53ad2c6f..13d26c85503b 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ProcessResourceComponentProvider.java @@ -20,6 +20,6 @@ @AutoService(ComponentProvider.class) public class ProcessResourceComponentProvider extends ResourceComponentProvider { public ProcessResourceComponentProvider() { - super("process", () -> ProcessResource.get().merge(ProcessRuntimeResource.get())); + super("process", p -> ProcessResource.get().merge(ProcessRuntimeResource.get())); } } diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java index 04508560f883..841839ccd03f 100644 --- a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/internal/ResourceComponentProvider.java @@ -8,15 +8,15 @@ import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.resources.Resource; -import java.util.function.Supplier; +import java.util.function.Function; /** Abstract class to simply {@link Resource} {@link ComponentProvider} implementations. */ abstract class ResourceComponentProvider implements ComponentProvider { private final String name; - private final Supplier supplier; + private final Function supplier; - ResourceComponentProvider(String name, Supplier supplier) { + ResourceComponentProvider(String name, Function supplier) { this.name = name; this.supplier = supplier; } @@ -33,6 +33,6 @@ public String getName() { @Override public Resource create(DeclarativeConfigProperties declarativeConfigProperties) { - return supplier.get(); + return supplier.apply(declarativeConfigProperties); } } diff --git a/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java deleted file mode 100644 index b32f6c361524..000000000000 --- a/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.resources; - -final class ProcessArguments { - - static String[] getProcessArguments() { - return ProcessHandle.current().info().arguments().orElseGet(() -> new String[0]); - } - - private ProcessArguments() {} -} diff --git a/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java new file mode 100644 index 000000000000..2361d1f14080 --- /dev/null +++ b/instrumentation/resources/library/src/main/java9/io/opentelemetry/instrumentation/resources/internal/ProcessArguments.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class ProcessArguments { + + public static String[] getProcessArguments() { + return ProcessHandle.current().info().arguments().orElseGet(() -> new String[0]); + } + + private ProcessArguments() {} +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java index 2986337d892a..336850c8e78e 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java @@ -13,11 +13,8 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import java.io.InputStream; import java.util.Collection; import java.util.Collections; -import java.util.Optional; -import java.util.jar.Manifest; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.DynamicTest; @@ -29,14 +26,14 @@ private static class TestCase { private final String name; private final String expectedName; private final String expectedVersion; - private final InputStream input; + private final Resource input; private final Resource existing; TestCase( String name, String expectedName, String expectedVersion, - InputStream input, + Resource input, Resource existing) { this.name = name; this.expectedName = expectedName; @@ -55,20 +52,17 @@ Collection createResource() { "name ok", "demo", "0.0.1-SNAPSHOT", - openClasspathResource("MANIFEST.MF"), + Resource.create( + Attributes.of(SERVICE_NAME, "demo", SERVICE_VERSION, "0.0.1-SNAPSHOT")), Resource.getDefault()), - new TestCase("name - no resource", null, null, null, Resource.getDefault()), new TestCase( - "name - empty resource", - null, - null, - openClasspathResource("empty-MANIFEST.MF"), - Resource.getDefault()), + "name - empty resource", null, null, Resource.empty(), Resource.getDefault()), new TestCase( "name already detected", null, "0.0.1-SNAPSHOT", - openClasspathResource("MANIFEST.MF"), + Resource.create( + Attributes.of(SERVICE_NAME, "demo", SERVICE_VERSION, "0.0.1-SNAPSHOT")), Resource.create(Attributes.of(SERVICE_NAME, "old")))) .map( t -> @@ -76,20 +70,7 @@ Collection createResource() { t.name, () -> { ManifestResourceProvider provider = - new ManifestResourceProvider( - new MainJarPathFinder( - () -> JarServiceNameDetectorTest.getArgs("app.jar"), - prop -> null, - JarServiceNameDetectorTest::failPath), - p -> { - try { - Manifest manifest = new Manifest(); - manifest.read(t.input); - return Optional.of(manifest); - } catch (Exception e) { - return Optional.empty(); - } - }); + new ManifestResourceProvider(() -> t.input); provider.shouldApply(config, t.existing); Resource resource = provider.createResource(config); @@ -99,8 +80,4 @@ Collection createResource() { })) .collect(Collectors.toList()); } - - private static InputStream openClasspathResource(String resource) { - return ManifestResourceProviderTest.class.getClassLoader().getResourceAsStream(resource); - } } diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractorTest.java similarity index 62% rename from instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java rename to instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractorTest.java index ff8ce66848ef..0a9596b0f1ce 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameDetectorTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/JarServiceNameResourceExtractorTest.java @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.resources; +package io.opentelemetry.instrumentation.resources.internal; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; import static org.junit.jupiter.params.provider.Arguments.arguments; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; import java.nio.file.Path; import java.nio.file.Paths; @@ -21,74 +20,74 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) // todo split JarFileDetectorTest and JarServiceNameDetectorTest -class JarServiceNameDetectorTest { - - @Mock ConfigProperties config; +class JarServiceNameResourceExtractorTest { @Test - void createResource_empty() { + void extractResource_empty() { String[] processArgs = new String[0]; Function getProperty = prop -> null; - Predicate fileExists = JarServiceNameDetectorTest::failPath; - JarServiceNameDetector serviceNameProvider = getDetector(processArgs, getProperty, fileExists); + Predicate fileExists = JarServiceNameResourceExtractorTest::failPath; + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(processArgs, getProperty, fileExists); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).isEmpty(); } - private static JarServiceNameDetector getDetector( + private static JarServiceNameResourceExtractor getExtractor( String[] processArgs, Function getProperty, Predicate fileExists) { - return new JarServiceNameDetector( + return new JarServiceNameResourceExtractor( new MainJarPathFinder(() -> processArgs, getProperty, fileExists)); } @Test - void createResource_noJarFileInArgs() { + void extractResource_noJarFileInArgs() { String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar"}; - JarServiceNameDetector serviceNameProvider = - getDetector(args, prop -> null, JarServiceNameDetectorTest::failPath); + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(args, prop -> null, JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).isEmpty(); } @Test - void createResource_processHandleJar() { - JarServiceNameDetector serviceNameProvider = - getDetector(getArgs("my-service.jar"), prop -> null, JarServiceNameDetectorTest::failPath); + void extractResource_processHandleJar() { + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor( + getArgs("my-service.jar"), prop -> null, JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @Test - void createResource_processHandleJarExtraFlag() { + void extractResource_processHandleJarExtraFlag() { String path = Paths.get("path", "to", "app", "my-service.jar").toString(); - JarServiceNameDetector serviceNameProvider = - getDetector( + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor( new String[] {"-Dtest=42", "-jar", "-Xmx512m", path, "abc", "def"}, prop -> null, - JarServiceNameDetectorTest::failPath); + JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @Test - void createResource_processHandleJarWithoutExtension() { - JarServiceNameDetector serviceNameProvider = - getDetector(getArgs("my-service"), prop -> null, JarServiceNameDetectorTest::failPath); + void extractResource_processHandleJarWithoutExtension() { + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor( + getArgs("my-service"), prop -> null, JarServiceNameResourceExtractorTest::failPath); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @@ -100,15 +99,15 @@ static String[] getArgs(String jarName) { @ParameterizedTest @MethodSource("sunCommandLineArguments") - void createResource_sunCommandLine(String commandLine, Path jarPath) { + void extractResource_sunCommandLine(String commandLine, Path jarPath) { Function getProperty = key -> "sun.java.command".equals(key) ? commandLine : null; Predicate fileExists = jarPath::equals; - JarServiceNameDetector serviceNameProvider = - getDetector(new String[0], getProperty, fileExists); + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(new String[0], getProperty, fileExists); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).hasSize(1).containsEntry(SERVICE_NAME, "my-service"); } @@ -116,15 +115,15 @@ void createResource_sunCommandLine(String commandLine, Path jarPath) { // regression test for // https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/7567 @Test - void createResource_sunCommandLineProblematicArgs() { + void extractResource_sunCommandLineProblematicArgs() { Function getProperty = key -> key.equals("sun.java.command") ? "one C:/two" : null; Predicate fileExists = path -> false; - JarServiceNameDetector serviceNameProvider = - getDetector(new String[0], getProperty, fileExists); + JarServiceNameResourceExtractor serviceNameProvider = + getExtractor(new String[0], getProperty, fileExists); - Resource resource = serviceNameProvider.createResource(config); + Resource resource = serviceNameProvider.extract(); assertThat(resource.getAttributes()).isEmpty(); } diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractorTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractorTest.java new file mode 100644 index 000000000000..0aa8544a96cd --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ManifestResourceExtractorTest.java @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources.internal; + +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_VERSION; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.resources.Resource; +import java.io.InputStream; +import java.util.Collection; +import java.util.Optional; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +class ManifestResourceExtractorTest { + + private static class TestCase { + private final String name; + private final String expectedName; + private final String expectedVersion; + private final InputStream input; + + TestCase(String name, String expectedName, String expectedVersion, InputStream input) { + this.name = name; + this.expectedName = expectedName; + this.expectedVersion = expectedVersion; + this.input = input; + } + } + + @TestFactory + Collection extractResource() { + return Stream.of( + new TestCase("name ok", "demo", "0.0.1-SNAPSHOT", openClasspathResource("MANIFEST.MF")), + new TestCase("name - no resource", null, null, null), + new TestCase( + "name - empty resource", null, null, openClasspathResource("empty-MANIFEST.MF"))) + .map( + t -> + DynamicTest.dynamicTest( + t.name, + () -> { + Resource resource = + new ManifestResourceExtractor( + new MainJarPathFinder( + () -> JarServiceNameResourceExtractorTest.getArgs("app.jar"), + prop -> null, + JarServiceNameResourceExtractorTest::failPath), + p -> { + try { + Manifest manifest = new Manifest(); + manifest.read(t.input); + return Optional.of(manifest); + } catch (Exception e) { + return Optional.empty(); + } + }) + .extract(); + assertThat(resource.getAttribute(SERVICE_NAME)).isEqualTo(t.expectedName); + assertThat(resource.getAttribute(SERVICE_VERSION)) + .isEqualTo(t.expectedVersion); + })) + .collect(Collectors.toList()); + } + + private static InputStream openClasspathResource(String resource) { + return ManifestResourceExtractorTest.class.getClassLoader().getResourceAsStream(resource); + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/DeclarativeConfigTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceDeclarativeConfigTest.java similarity index 98% rename from instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/DeclarativeConfigTest.java rename to instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceDeclarativeConfigTest.java index 4033d6173235..882dc9af7539 100644 --- a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/DeclarativeConfigTest.java +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/internal/ResourceDeclarativeConfigTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -class DeclarativeConfigTest { +class ResourceDeclarativeConfigTest { // just to ensure that the test exporters are registered @RegisterExtension 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 9754ccb3b7ad..02433342b1ab 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 @@ -105,7 +105,6 @@ void mapObjectHeaders(String key) { public static Stream listProperties() { return Stream.of( - Arguments.of("otel.experimental.metrics.view.config", Arrays.asList("a", "b")), Arguments.of("otel.experimental.resource.disabled.keys", Arrays.asList("a", "b")), Arguments.of("otel.propagators", Arrays.asList("baggage", "b3")), Arguments.of("otel.logs.exporter", Collections.singletonList("console")), diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml b/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml index c9485005a6a7..bfbad4f868be 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/resources/application.yaml @@ -1,8 +1,5 @@ otel: experimental: - metrics: - view: - config: [ a,b ] resource: disabled: keys: [ a,b ] diff --git a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java index 0bcdb527095a..c54542fdf9f7 100644 --- a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java +++ b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java @@ -9,6 +9,7 @@ import static java.util.logging.Level.FINER; import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; @@ -71,7 +72,14 @@ public SpringBootServiceNameDetector() { @Override public Resource createResource(ConfigProperties config) { + return create(); + } + + Resource createResource(DeclarativeConfigProperties config) { + return create(); + } + private Resource create() { logger.log(FINER, "Performing Spring Boot service name auto-detection..."); // Note: The order should be consistent with the order of Spring matching, but noting // that we have "first one wins" while Spring has "last one wins". diff --git a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java index 865279de3064..f888deda2d3f 100644 --- a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java +++ b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java @@ -8,6 +8,7 @@ import static java.util.logging.Level.FINE; import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; @@ -41,6 +42,14 @@ public SpringBootServiceVersionDetector() { @Override public Resource createResource(ConfigProperties config) { + return create(); + } + + Resource createResource(DeclarativeConfigProperties config) { + return create(); + } + + private Resource create() { return getServiceVersionFromBuildInfo() .map( version -> { diff --git a/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringResourceComponentProvider.java b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringResourceComponentProvider.java new file mode 100644 index 000000000000..a6eff7234e59 --- /dev/null +++ b/instrumentation/spring/spring-boot-resources/javaagent/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringResourceComponentProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** + * Declarative config spring resource provider. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class SpringResourceComponentProvider implements ComponentProvider { + + @Override + public Class getType() { + return Resource.class; + } + + @Override + public String getName() { + return "spring"; + } + + @Override + public Resource create(DeclarativeConfigProperties config) { + return new SpringBootServiceVersionDetector() + .createResource(config) + .merge(new SpringBootServiceNameDetector().createResource(config)); + } +} diff --git a/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy b/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy index a0a73390d036..27300475fd6e 100644 --- a/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy +++ b/javaagent-bootstrap/src/test/groovy/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.groovy @@ -56,7 +56,7 @@ class AgentClassLoaderTest extends Specification { def "multi release jar"() { setup: boolean jdk8 = "1.8" == System.getProperty("java.specification.version") - def mrJarClass = Class.forName("io.opentelemetry.instrumentation.resources.ProcessArguments") + def mrJarClass = Class.forName("io.opentelemetry.instrumentation.resources.internal.ProcessArguments") // sdk is a multi release jar URL multiReleaseJar = mrJarClass.getProtectionDomain().getCodeSource().getLocation() AgentClassLoader loader = new AgentClassLoader(new File(multiReleaseJar.toURI())) { @@ -67,7 +67,7 @@ class AgentClassLoaderTest extends Specification { } when: - URL url = loader.findResource("io/opentelemetry/instrumentation/resources/ProcessArguments.class") + URL url = loader.findResource("io/opentelemetry/instrumentation/resources/internal/ProcessArguments.class") then: url != null @@ -75,7 +75,7 @@ class AgentClassLoaderTest extends Specification { jdk8 != url.toString().contains("META-INF/versions/9/") and: - Class clazz = loader.loadClass("io.opentelemetry.instrumentation.resources.ProcessArguments") + Class clazz = loader.loadClass("io.opentelemetry.instrumentation.resources.internal.ProcessArguments") // class was loaded by agent loader used in this test clazz.getClassLoader() == loader Method method = clazz.getDeclaredMethod("getProcessArguments") diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index 653ff1bdf1ce..848c87e7da82 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -62,6 +62,7 @@ dependencies { testImplementation(project(":testing-common")) testImplementation("com.google.guava:guava") testImplementation("org.junit-pioneer:junit-pioneer") + testImplementation("com.fasterxml.jackson.core:jackson-databind") } testing { diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroComponentProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroComponentProvider.java new file mode 100644 index 000000000000..498b0474863e --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroComponentProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.resources.Resource; + +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class DistroComponentProvider implements ComponentProvider { + + @Override + public Class getType() { + return Resource.class; + } + + @Override + public String getName() { + return "opentelemetry-javaagent-distribution"; + } + + @Override + public Resource create(DeclarativeConfigProperties config) { + return DistroResourceProvider.get("opentelemetry-javaagent"); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DistroVersionResourceProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroResourceProvider.java similarity index 67% rename from javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DistroVersionResourceProvider.java rename to javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroResourceProvider.java index 53fe1d65a5d1..782878082ea7 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DistroVersionResourceProvider.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/DistroResourceProvider.java @@ -3,29 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.tooling; +package io.opentelemetry.javaagent.tooling.resources; import static io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME; import static io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION; import com.google.auto.service.AutoService; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.javaagent.tooling.AgentVersion; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; @AutoService(ResourceProvider.class) -public class DistroVersionResourceProvider implements ResourceProvider { +public class DistroResourceProvider implements ResourceProvider { @Override public Resource createResource(ConfigProperties config) { + return get("opentelemetry-java-instrumentation"); + } + + static Resource get(String distroName) { return AgentVersion.VERSION == null ? Resource.empty() : Resource.create( Attributes.of( - TELEMETRY_DISTRO_NAME, - "opentelemetry-java-instrumentation", - TELEMETRY_DISTRO_VERSION, - AgentVersion.VERSION)); + TELEMETRY_DISTRO_NAME, distroName, TELEMETRY_DISTRO_VERSION, AgentVersion.VERSION)); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProvider.java new file mode 100644 index 000000000000..0beebe01d112 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProvider.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.resources; + +import com.google.auto.service.AutoService; +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; + +/** + * Adds essential resource detectors to the resource model in declarative configuration, if they are + * not already present. + */ +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +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-javaagent-distribution"); + + @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/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProviderTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProviderTest.java new file mode 100644 index 000000000000..5b50cc913437 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/resources/ResourceCustomizerProviderTest.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import org.junit.jupiter.api.Test; + +class ResourceCustomizerProviderTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void customize() { + new ResourceCustomizerProvider() + .customize( + customizer -> { + OpenTelemetryConfigurationModel configurationModel = + customizer.apply(new OpenTelemetryConfigurationModel()); + + try { + assertThat(objectMapper.writeValueAsString(configurationModel.getResource())) + .isEqualTo( + "{\"attributes\":[],\"detection/development\":{\"detectors\":[{\"opentelemetry-javaagent-distribution\":null}]}}"); + } catch (JsonProcessingException e) { + throw new AssertionError(e); + } + }); + } +} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy new file mode 100644 index 000000000000..54fd5c6563a0 --- /dev/null +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/DeclarativeConfigurationSmokeTest.groovy @@ -0,0 +1,74 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.smoketest + + +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest +import spock.lang.IgnoreIf +import spock.lang.Unroll + +import java.time.Duration + +import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers + +@IgnoreIf({ useWindowsContainers() }) +class DeclarativeConfigurationSmokeTest extends SmokeTest { + + protected String getTargetImage(String jdk) { + "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-spring-boot:jdk$jdk-20241021.11448062567" + } + + @Override + protected List getExtraResources() { + [ + ResourceMapping.of("declarative-config.yaml", "/declarative-config.yaml"), + ] + } + + @Override + protected Map getExtraEnv() { + return ["OTEL_EXPERIMENTAL_CONFIG_FILE": "declarative-config.yaml"] + } + + @Override + protected TargetWaitStrategy getWaitStrategy() { + return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Started SpringbootApplication in.*") + } + + @Unroll + def "spring boot smoke test on JDK #jdk"(int jdk) { + setup: + startTarget(jdk) + + when: + client().get("/greeting").aggregate().join() + Collection traces = waitForTraces() + + then: "There is one trace" + traces.size() > 0 + + then: "explicitly set attribute is present" + hasResourceAttribute(traces, "service.name", "declarative-config-smoke-test") + + then: "explicitly set container detector is used" + findResourceAttribute(traces, "container.id").findAny().isPresent() + + then: "explicitly set container process detector is used" + findResourceAttribute(traces, "process.executable.path").findAny().isPresent() + + then: "explicitly set container host detector is used" + findResourceAttribute(traces, "host.name").findAny().isPresent() + + then: "distro detector is added by customizer" + hasResourceAttribute(traces, "telemetry.distro.name", "opentelemetry-javaagent") + + cleanup: + stopTarget() + + where: + jdk << [8, 11, 17] + } +} diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy index d8bdee7dfa3b..d2f6f53e613f 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy @@ -118,6 +118,15 @@ abstract class SmokeTest extends Specification { .map { it.value } } + protected static boolean hasResourceAttribute(Collection traces, + String attributeKey, + String attributeValue) { + return findResourceAttribute(traces, attributeKey) + .filter { it.stringValue == attributeValue } + .findAny() + .isPresent() + } + protected static int countSpansByName(Collection traces, String spanName) { return getSpanStream(traces).filter { it.name == spanName }.count() } diff --git a/smoke-tests/src/test/resources/declarative-config.yaml b/smoke-tests/src/test/resources/declarative-config.yaml new file mode 100644 index 000000000000..ce39f4c3a830 --- /dev/null +++ b/smoke-tests/src/test/resources/declarative-config.yaml @@ -0,0 +1,21 @@ +file_format: 1.0-rc.1 + +tracer_provider: + processors: + - batch: + schedule_delay: 10 + max_export_batch_size: 1 + exporter: + otlp_grpc: + endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT} + +# add extra resource attributes to verify that declarative config is picked up +resource: + detection/development: + detectors: + - container: + - host: + - process: + attributes: + - name: service.name + value: declarative-config-smoke-test