diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeProvider.java new file mode 100644 index 000000000000..0e74368ff3fd --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeProvider.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.common.AttributeKey; +import java.util.Optional; +import java.util.function.Function; + +/** + * An easier alternative to {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider}, which + * avoids some common pitfalls and boilerplate. + * + *

An example of how to use this interface can be found in {@link ManifestResourceProvider}. + */ +interface AttributeProvider { + Optional readData(); + + void registerAttributes(Builder builder); + + interface Builder { + @CanIgnoreReturnValue + Builder add(AttributeKey key, Function> getter); + } +} 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 new file mode 100644 index 000000000000..e8ab69d2d3c5 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java @@ -0,0 +1,116 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +/** + * An easier alternative to {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider}, which + * avoids some common pitfalls and boilerplate. + * + *

An example of how to use this interface can be found in {@link ManifestResourceProvider}. + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public abstract class AttributeResourceProvider implements ConditionalResourceProvider { + + private final AttributeProvider attributeProvider; + + public class AttributeBuilder implements AttributeProvider.Builder { + + private AttributeBuilder() {} + + @CanIgnoreReturnValue + @Override + public AttributeBuilder add(AttributeKey key, Function> getter) { + attributeGetters.put((AttributeKey) key, Objects.requireNonNull((Function) getter)); + return this; + } + } + + private static final ThreadLocal existingResource = new ThreadLocal<>(); + + private final Map, Function>> attributeGetters = + new HashMap<>(); + + public AttributeResourceProvider(AttributeProvider attributeProvider) { + this.attributeProvider = attributeProvider; + attributeProvider.registerAttributes(new AttributeBuilder()); + } + + @Override + public final boolean shouldApply(ConfigProperties config, Resource existing) { + existingResource.set(existing); + + Map resourceAttributes = getResourceAttributes(config); + return attributeGetters.keySet().stream() + .allMatch(key -> shouldUpdate(config, existing, key, resourceAttributes)); + } + + @Override + public final Resource createResource(ConfigProperties config) { + return attributeProvider + .readData() + .map( + data -> { + // what should we do here? + // we don't have access to the existing resource + // if the resource provider produces a single key, we can rely on shouldApply + // i.e. this method won't be called if the key is already present + // the thread local is a hack to work around this + Resource existing = + Objects.requireNonNull(existingResource.get(), "call shouldApply first"); + Map resourceAttributes = getResourceAttributes(config); + AttributesBuilder builder = Attributes.builder(); + attributeGetters.entrySet().stream() + .filter(e -> shouldUpdate(config, existing, e.getKey(), resourceAttributes)) + .forEach( + e -> + e.getValue() + .apply(data) + .ifPresent(value -> putAttribute(builder, e.getKey(), value))); + return Resource.create(builder.build()); + }) + .orElse(Resource.empty()); + } + + private static void putAttribute(AttributesBuilder builder, AttributeKey key, T value) { + builder.put(key, value); + } + + private static Map getResourceAttributes(ConfigProperties config) { + return config.getMap("otel.resource.attributes"); + } + + private static boolean shouldUpdate( + ConfigProperties config, + Resource existing, + AttributeKey key, + Map resourceAttributes) { + if (resourceAttributes.containsKey(key.getKey())) { + return false; + } + + Object value = existing.getAttribute(key); + + if (key.equals(ResourceAttributes.SERVICE_NAME)) { + return config.getString("otel.service.name") == null && "unknown_service:java".equals(value); + } + + return value == null; + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarPathFinder.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarPathFinder.java new file mode 100644 index 000000000000..0c25176a6701 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/JarPathFinder.java @@ -0,0 +1,112 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +class JarPathFinder { + private final Supplier getProcessHandleArguments; + private final Function getSystemProperty; + private final Predicate fileExists; + + private static class DetectionResult { + private final Optional jarPath; + + private DetectionResult(Optional jarPath) { + this.jarPath = jarPath; + } + } + + private static Optional detectionResult = Optional.empty(); + + public JarPathFinder() { + this(ProcessArguments::getProcessArguments, System::getProperty, Files::isRegularFile); + } + + // visible for tests + JarPathFinder( + Supplier getProcessHandleArguments, + Function getSystemProperty, + Predicate fileExists) { + this.getProcessHandleArguments = getProcessHandleArguments; + this.getSystemProperty = getSystemProperty; + this.fileExists = fileExists; + } + + // visible for testing + static void resetForTest() { + detectionResult = Optional.empty(); + } + + Optional getJarPath() { + if (!detectionResult.isPresent()) { + detectionResult = Optional.of(new DetectionResult(Optional.ofNullable(detectJarPath()))); + } + return detectionResult.get().jarPath; + } + + private Path detectJarPath() { + Path jarPath = getJarPathFromProcessHandle(); + if (jarPath != null) { + return jarPath; + } + return getJarPathFromSunCommandLine(); + } + + @Nullable + private Path getJarPathFromProcessHandle() { + String[] javaArgs = getProcessHandleArguments.get(); + for (int i = 0; i < javaArgs.length; ++i) { + if ("-jar".equals(javaArgs[i]) && (i < javaArgs.length - 1)) { + return Paths.get(javaArgs[i + 1]); + } + } + return null; + } + + @Nullable + private Path getJarPathFromSunCommandLine() { + // the jar file is the first argument in the command line string + String programArguments = getSystemProperty.apply("sun.java.command"); + if (programArguments == null) { + return null; + } + + // Take the path until the first space. If the path doesn't exist extend it up to the next + // space. Repeat until a path that exists is found or input runs out. + int next = 0; + while (true) { + int nextSpace = programArguments.indexOf(' ', next); + if (nextSpace == -1) { + return pathIfExists(programArguments); + } + Path path = pathIfExists(programArguments.substring(0, nextSpace)); + next = nextSpace + 1; + if (path != null) { + return path; + } + } + } + + @Nullable + private Path pathIfExists(String programArguments) { + Path candidate; + try { + candidate = Paths.get(programArguments); + } catch (InvalidPathException e) { + return null; + } + return fileExists.test(candidate) ? candidate : null; + } +} 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 f77b7e8abd46..51b379f2926a 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 @@ -14,16 +14,9 @@ import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ResourceAttributes; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.logging.Logger; -import javax.annotation.Nullable; /** * A {@link ResourceProvider} that will attempt to detect the application name from the jar name. @@ -33,37 +26,30 @@ public final class JarServiceNameDetector implements ConditionalResourceProvider private static final Logger logger = Logger.getLogger(JarServiceNameDetector.class.getName()); - private final Supplier getProcessHandleArguments; - private final Function getSystemProperty; - private final Predicate fileExists; + private final JarPathFinder jarPathFinder; @SuppressWarnings("unused") // SPI public JarServiceNameDetector() { - this(ProcessArguments::getProcessArguments, System::getProperty, Files::isRegularFile); + this(new JarPathFinder()); } // visible for tests - JarServiceNameDetector( - Supplier getProcessHandleArguments, - Function getSystemProperty, - Predicate fileExists) { - this.getProcessHandleArguments = getProcessHandleArguments; - this.getSystemProperty = getSystemProperty; - this.fileExists = fileExists; + JarServiceNameDetector(JarPathFinder jarPathFinder) { + this.jarPathFinder = jarPathFinder; } @Override public Resource createResource(ConfigProperties config) { - Path jarPath = getJarPathFromProcessHandle(); - if (jarPath == null) { - jarPath = getJarPathFromSunCommandLine(); - } - if (jarPath == null) { - return Resource.empty(); - } - String serviceName = getServiceName(jarPath); - logger.log(FINE, "Auto-detected service name from the jar file name: {0}", serviceName); - return Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, serviceName)); + return jarPathFinder + .getJarPath() + .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(ResourceAttributes.SERVICE_NAME, serviceName)); + }) + .orElseGet(Resource::empty); } @Override @@ -75,52 +61,6 @@ public boolean shouldApply(ConfigProperties config, Resource existing) { && "unknown_service:java".equals(existing.getAttribute(ResourceAttributes.SERVICE_NAME)); } - @Nullable - private Path getJarPathFromProcessHandle() { - String[] javaArgs = getProcessHandleArguments.get(); - for (int i = 0; i < javaArgs.length; ++i) { - if ("-jar".equals(javaArgs[i]) && (i < javaArgs.length - 1)) { - return Paths.get(javaArgs[i + 1]); - } - } - return null; - } - - @Nullable - private Path getJarPathFromSunCommandLine() { - // the jar file is the first argument in the command line string - String programArguments = getSystemProperty.apply("sun.java.command"); - if (programArguments == null) { - return null; - } - - // Take the path until the first space. If the path doesn't exist extend it up to the next - // space. Repeat until a path that exists is found or input runs out. - int next = 0; - while (true) { - int nextSpace = programArguments.indexOf(' ', next); - if (nextSpace == -1) { - return pathIfExists(programArguments); - } - Path path = pathIfExists(programArguments.substring(0, nextSpace)); - next = nextSpace + 1; - if (path != null) { - return path; - } - } - } - - @Nullable - private Path pathIfExists(String programArguments) { - Path candidate; - try { - candidate = Paths.get(programArguments); - } catch (InvalidPathException e) { - return null; - } - return fileExists.test(candidate) ? candidate : null; - } - private static String getServiceName(Path jarPath) { String jarName = jarPath.getFileName().toString(); int dotIndex = jarName.lastIndexOf("."); 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 new file mode 100644 index 000000000000..461af3cd99c7 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ManifestResourceProvider.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static java.util.logging.Level.WARNING; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.semconv.ResourceAttributes; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Path; +import java.util.Optional; +import java.util.function.Function; +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()); + + @SuppressWarnings("unused") // SPI + public ManifestResourceProvider() { + this(new JarPathFinder(), ManifestResourceProvider::readManifest); + } + + // Visible for testing + ManifestResourceProvider( + JarPathFinder jarPathFinder, Function> manifestReader) { + super( + new AttributeProvider() { + @Override + public Optional readData() { + return jarPathFinder.getJarPath().flatMap(manifestReader); + } + + @Override + public void registerAttributes(Builder builder) { + builder + .add( + ResourceAttributes.SERVICE_NAME, + manifest -> { + String serviceName = + manifest.getMainAttributes().getValue("Implementation-Title"); + return Optional.ofNullable(serviceName); + }) + .add( + ResourceAttributes.SERVICE_VERSION, + manifest -> { + String serviceVersion = + manifest.getMainAttributes().getValue("Implementation-Version"); + return Optional.ofNullable(serviceVersion); + }); + } + }); + } + + private static Optional readManifest(Path jarPath) { + try (InputStream s = + new URL(String.format("jar:%s!/META-INF/MANIFEST.MF", jarPath.toUri())).openStream()) { + Manifest manifest = new Manifest(); + manifest.read(s); + return Optional.of(manifest); + } catch (Exception e) { + logger.log(WARNING, "Error reading manifest", e); + return Optional.empty(); + } + } + + @Override + public int order() { + // make it run later than ManifestResourceProvider and SpringBootServiceNameDetector + return 300; + } +} 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/JarServiceNameDetectorTest.java index d9b6e8b9afe0..dd9562d572cd 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/JarServiceNameDetectorTest.java @@ -16,6 +16,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; @@ -27,26 +28,39 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) +// todo split JarFileDetectorTest and JarServiceNameDetectorTest class JarServiceNameDetectorTest { @Mock ConfigProperties config; + @BeforeEach + void setUp() { + JarPathFinder.resetForTest(); + } + @Test void createResource_empty() { - JarServiceNameDetector serviceNameProvider = - new JarServiceNameDetector( - () -> new String[0], prop -> null, JarServiceNameDetectorTest::failPath); + String[] processArgs = new String[0]; + Function getProperty = prop -> null; + Predicate fileExists = JarServiceNameDetectorTest::failPath; + JarServiceNameDetector serviceNameProvider = getDetector(processArgs, getProperty, fileExists); Resource resource = serviceNameProvider.createResource(config); assertThat(resource.getAttributes()).isEmpty(); } + private static JarServiceNameDetector getDetector( + String[] processArgs, Function getProperty, Predicate fileExists) { + return new JarServiceNameDetector( + new JarPathFinder(() -> processArgs, getProperty, fileExists)); + } + @Test void createResource_noJarFileInArgs() { String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar"}; JarServiceNameDetector serviceNameProvider = - new JarServiceNameDetector(() -> args, prop -> null, JarServiceNameDetectorTest::failPath); + getDetector(args, prop -> null, JarServiceNameDetectorTest::failPath); Resource resource = serviceNameProvider.createResource(config); @@ -55,10 +69,8 @@ void createResource_noJarFileInArgs() { @Test void createResource_processHandleJar() { - String path = Paths.get("path", "to", "app", "my-service.jar").toString(); - String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar", path, "abc", "def"}; JarServiceNameDetector serviceNameProvider = - new JarServiceNameDetector(() -> args, prop -> null, JarServiceNameDetectorTest::failPath); + getDetector(getArgs("my-service.jar"), prop -> null, JarServiceNameDetectorTest::failPath); Resource resource = serviceNameProvider.createResource(config); @@ -69,10 +81,8 @@ void createResource_processHandleJar() { @Test void createResource_processHandleJarWithoutExtension() { - String path = Paths.get("path", "to", "app", "my-service.jar").toString(); - String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar", path}; JarServiceNameDetector serviceNameProvider = - new JarServiceNameDetector(() -> args, prop -> null, JarServiceNameDetectorTest::failPath); + getDetector(getArgs("my-service"), prop -> null, JarServiceNameDetectorTest::failPath); Resource resource = serviceNameProvider.createResource(config); @@ -81,6 +91,11 @@ void createResource_processHandleJarWithoutExtension() { .containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); } + static String[] getArgs(String jarName) { + String path = Paths.get("path", "to", "app", jarName).toString(); + return new String[] {"-Dtest=42", "-Xmx666m", "-jar", path, "abc", "def"}; + } + @ParameterizedTest @ArgumentsSource(SunCommandLineProvider.class) void createResource_sunCommandLine(String commandLine, Path jarPath) { @@ -89,7 +104,7 @@ void createResource_sunCommandLine(String commandLine, Path jarPath) { Predicate fileExists = jarPath::equals; JarServiceNameDetector serviceNameProvider = - new JarServiceNameDetector(() -> new String[0], getProperty, fileExists); + getDetector(new String[0], getProperty, fileExists); Resource resource = serviceNameProvider.createResource(config); @@ -107,7 +122,7 @@ void createResource_sunCommandLineProblematicArgs() { Predicate fileExists = path -> false; JarServiceNameDetector serviceNameProvider = - new JarServiceNameDetector(() -> new String[0], getProperty, fileExists); + getDetector(new String[0], getProperty, fileExists); Resource resource = serviceNameProvider.createResource(config); @@ -128,7 +143,7 @@ public Stream provideArguments(ExtensionContext context) { } } - private static boolean failPath(Path file) { + static boolean failPath(Path file) { throw new AssertionError("Unexpected call to Files.isRegularFile()"); } } 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 new file mode 100644 index 000000000000..d789dbb0ffff --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ManifestResourceProviderTest.java @@ -0,0 +1,89 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static io.opentelemetry.semconv.ResourceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.ResourceAttributes.SERVICE_VERSION; +import static org.assertj.core.api.Assertions.assertThat; + +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.BeforeEach; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +class ManifestResourceProviderTest { + + @BeforeEach + void setUp() { + JarPathFinder.resetForTest(); + } + + private static class TestCase { + private final String name; + private final String expectedName; + private final String expectedVersion; + private final InputStream input; + + public TestCase(String name, String expectedName, String expectedVersion, InputStream input) { + this.name = name; + this.expectedName = expectedName; + this.expectedVersion = expectedVersion; + this.input = input; + } + } + + @TestFactory + Collection createResource() { + ConfigProperties config = DefaultConfigProperties.createFromMap(Collections.emptyMap()); + + 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, + () -> { + ManifestResourceProvider provider = + new ManifestResourceProvider( + new JarPathFinder( + () -> 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(); + } + }); + provider.shouldApply(config, Resource.getDefault()); + + Resource resource = provider.createResource(config); + 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 ManifestResourceProviderTest.class.getClassLoader().getResourceAsStream(resource); + } +} diff --git a/instrumentation/resources/library/src/test/resources/MANIFEST.MF b/instrumentation/resources/library/src/test/resources/MANIFEST.MF new file mode 100644 index 000000000000..28f63e3a3517 --- /dev/null +++ b/instrumentation/resources/library/src/test/resources/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Implementation-Title: demo +Implementation-Version: 0.0.1-SNAPSHOT diff --git a/instrumentation/resources/library/src/test/resources/empty-MANIFEST.MF b/instrumentation/resources/library/src/test/resources/empty-MANIFEST.MF new file mode 100644 index 000000000000..9d885be53412 --- /dev/null +++ b/instrumentation/resources/library/src/test/resources/empty-MANIFEST.MF @@ -0,0 +1 @@ +Manifest-Version: 1.0 diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/QuarkusSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/QuarkusSmokeTest.groovy index 3eb71b6e1b21..9279197eefb2 100644 --- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/QuarkusSmokeTest.groovy +++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/QuarkusSmokeTest.groovy @@ -5,7 +5,7 @@ package io.opentelemetry.smoketest -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest +import io.opentelemetry.semconv.ResourceAttributes import spock.lang.IgnoreIf import spock.lang.Unroll @@ -14,7 +14,6 @@ import java.util.jar.Attributes import java.util.jar.JarFile import static io.opentelemetry.smoketest.TestContainerManager.useWindowsContainers -import static java.util.stream.Collectors.toSet @IgnoreIf({ useWindowsContainers() }) class QuarkusSmokeTest extends SmokeTest { @@ -28,6 +27,11 @@ class QuarkusSmokeTest extends SmokeTest { return new TargetWaitStrategy.Log(Duration.ofMinutes(1), ".*Listening on.*") } + @Override + protected boolean getSetServiceName() { + return false + } + @Unroll def "quarkus smoke test on JDK #jdk"(int jdk) { setup: @@ -37,14 +41,16 @@ class QuarkusSmokeTest extends SmokeTest { when: client().get("/hello").aggregate().join() - Collection traces = waitForTraces() + TraceInspector traces = new TraceInspector(waitForTraces()) + + then: "Expected span names" + traces.countSpansByName('GET /hello') == 1 - then: - countSpansByName(traces, 'GET /hello') == 1 + and: "telemetry.distro.version is set" + traces.countFilteredResourceAttributes("telemetry.distro.version", currentAgentVersion) == 1 - [currentAgentVersion] as Set == findResourceAttribute(traces, "telemetry.distro.version") - .map { it.stringValue } - .collect(toSet()) + and: "service.name is detected from manifest" + traces.countFilteredResourceAttributes(ResourceAttributes.SERVICE_NAME.key, "smoke-test-quarkus-images") == 1 cleanup: stopTarget()