diff --git a/.gitignore b/.gitignore index 6c4640f68103..6eb86aa73185 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ target !**/gradle/wrapper/* .gradle **/build/ +**/generated/ examples/**/build/ # Eclipse # diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java index 41aab9620db8..0e5e86df5c45 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java @@ -9,6 +9,7 @@ import io.opentelemetry.instrumentation.resources.ContainerResourceProvider; import io.opentelemetry.instrumentation.resources.HostResource; import io.opentelemetry.instrumentation.resources.HostResourceProvider; +import io.opentelemetry.instrumentation.resources.JarServiceNameDetector; import io.opentelemetry.instrumentation.resources.OsResource; import io.opentelemetry.instrumentation.resources.OsResourceProvider; import io.opentelemetry.instrumentation.resources.ProcessResource; @@ -18,10 +19,13 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.sdk.autoconfigure.internal.EnvironmentResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.info.BuildProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -37,8 +41,9 @@ public ResourceProvider otelEnvironmentResourceProvider() { } @Bean - public ResourceProvider otelSpringResourceProvider() { - return new SpringResourceProvider(); + public ResourceProvider otelSpringResourceProvider( + @Autowired(required = false) BuildProperties buildProperties) { + return new SpringResourceProvider(Optional.ofNullable(buildProperties)); } @Bean @@ -46,6 +51,12 @@ public ResourceProvider otelDistroVersionResourceProvider() { return new DistroVersionResourceProvider(); } + @Bean + @ConditionalOnClass(JarServiceNameDetector.class) + public ResourceProvider otelJarResourceProvider() { + return new JarServiceNameDetector(); + } + @Bean @ConditionalOnClass(OsResource.class) public ResourceProvider otelOsResourceProvider() { diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProvider.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProvider.java index 756d227f818e..baf76a586ce2 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProvider.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProvider.java @@ -11,16 +11,33 @@ import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ResourceAttributes; +import java.util.Optional; +import org.springframework.boot.info.BuildProperties; public class SpringResourceProvider implements ResourceProvider { + private final Optional buildProperties; + + public SpringResourceProvider(Optional buildProperties) { + this.buildProperties = buildProperties; + } + @Override public Resource createResource(ConfigProperties configProperties) { AttributesBuilder attributesBuilder = Attributes.builder(); + buildProperties + .map(BuildProperties::getName) + .ifPresent(v -> attributesBuilder.put(ResourceAttributes.SERVICE_NAME, v)); + String springApplicationName = configProperties.getString("spring.application.name"); if (springApplicationName != null) { attributesBuilder.put(ResourceAttributes.SERVICE_NAME, springApplicationName); } + + buildProperties + .map(BuildProperties::getVersion) + .ifPresent(v -> attributesBuilder.put(ResourceAttributes.SERVICE_VERSION, v)); + return Resource.create(attributesBuilder.build()); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java index e0bf3f98f849..4c8cdd43ec02 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure; 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.api.OpenTelemetry; @@ -16,9 +17,11 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; +import java.util.Properties; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -117,6 +120,27 @@ void shouldDetermineServiceNameBySpringApplicationName() { }); } + @Test + @DisplayName( + "when spring.application.name is set value should be passed to service name attribute") + void shouldDetermineServiceNameAndVersionBySpringApplicationVersion() { + Properties properties = new Properties(); + properties.put("name", "demo"); + properties.put("version", "0.3"); + this.contextRunner + .withBean("buildProperties", BuildProperties.class, () -> new BuildProperties(properties)) + .withConfiguration( + AutoConfigurations.of( + OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class)) + .run( + context -> { + Resource otelResource = context.getBean("otelResource", Resource.class); + + assertThat(otelResource.getAttribute(SERVICE_NAME)).isEqualTo("demo"); + assertThat(otelResource.getAttribute(SERVICE_VERSION)).isEqualTo("0.3"); + }); + } + @Test @DisplayName( "when spring application name and otel service name are not set service name should be default") diff --git a/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java b/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java index b0a598846abe..02eb3897ba3c 100644 --- a/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java +++ b/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceNameDetector.java @@ -45,6 +45,9 @@ *
  • Check for --spring.application.name program argument (not jvm arg) via ProcessHandle *
  • Check for --spring.application.name program argument via sun.java.command system property * + * + *

    Note: should not be used inside a spring application, where the spring.application.name is + * already available. */ @AutoService(ResourceProvider.class) public class SpringBootServiceNameDetector implements ConditionalResourceProvider { diff --git a/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java b/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java index 3c9ddb05fcdd..b33c2a9a9167 100644 --- a/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java +++ b/instrumentation/spring/spring-boot-resources/library/src/main/java/io/opentelemetry/instrumentation/spring/resources/SpringBootServiceVersionDetector.java @@ -18,6 +18,10 @@ import java.util.Properties; import java.util.logging.Logger; +/** + * Note: should not be used inside a spring application, where the spring.application.name is + * already available. + */ @AutoService(ResourceProvider.class) public class SpringBootServiceVersionDetector implements ResourceProvider { diff --git a/smoke-tests-otel-starter/build.gradle.kts b/smoke-tests-otel-starter/build.gradle.kts index 062ad5b5debf..65c018987178 100644 --- a/smoke-tests-otel-starter/build.gradle.kts +++ b/smoke-tests-otel-starter/build.gradle.kts @@ -53,6 +53,10 @@ configurations.configureEach { exclude("org.spockframework", "spock-core") } +springBoot { + buildInfo() +} + graalvmNative { binaries.all { // Workaround for https://github.com/junit-team/junit5/issues/3405 diff --git a/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java index 5bfffb1f6f4f..2bc9dbe07791 100644 --- a/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java @@ -14,16 +14,19 @@ import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.sdk.testing.assertj.TracesAssert; import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.semconv.ResourceAttributes; import io.opentelemetry.semconv.SemanticAttributes; import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestApplication; import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestController; import java.util.List; +import org.assertj.core.api.AbstractCharSequenceAssert; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -109,6 +112,15 @@ void shouldSendTelemetry() throws InterruptedException { spanDataAssert -> spanDataAssert .hasKind(SpanKind.SERVER) + .hasResourceSatisfying( + r -> + r.hasAttribute( + ResourceAttributes.SERVICE_NAME, + "smoke-tests-otel-starter") + .hasAttribute( + OpenTelemetryAssertions.satisfies( + ResourceAttributes.SERVICE_VERSION, + AbstractCharSequenceAssert::isNotBlank))) .hasAttribute(SemanticAttributes.HTTP_REQUEST_METHOD, "GET") .hasAttribute(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, 200L) .hasAttribute(SemanticAttributes.HTTP_ROUTE, "/ping")));