diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java index 52c66a44856e..b468c18f4593 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java @@ -99,6 +99,7 @@ public void applyTo(BiConsumer consumer) { }); attributes.computeIfAbsent("service.name", (k) -> getApplicationName()); attributes.computeIfAbsent("service.group", (k) -> getApplicationGroup()); + attributes.computeIfAbsent("service.namespace", (key) -> getServiceNamespace()); attributes.forEach(consumer); } @@ -106,11 +107,18 @@ private String getApplicationName() { return this.environment.getProperty("spring.application.name", DEFAULT_SERVICE_NAME); } + @Deprecated(since = "3.5.0", forRemoval = true) + // See https://github.com/spring-projects/spring-boot/issues/44411 for potential + // information about deprecation of "service.group" attribute private String getApplicationGroup() { String applicationGroup = this.environment.getProperty("spring.application.group"); return (StringUtils.hasLength(applicationGroup)) ? applicationGroup : null; } + private String getServiceNamespace() { + return this.environment.getProperty("spring.application.group"); + } + /** * Parses resource attributes from the {@link System#getenv()}. This method fetches * attributes defined in the {@code OTEL_RESOURCE_ATTRIBUTES} and diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java index 14ab7b3c6380..dba908b68191 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java @@ -106,6 +106,22 @@ void shouldNotApplySpringApplicationGroupIfNotSet() { }); } + @Test + void shouldApplyServiceNamespaceIfApplicationGroupIsSet() { + this.runner.withPropertyValues("spring.application.group=my-group").run((context) -> { + Resource resource = context.getBean(Resource.class); + assertThat(resource.getAttributes().asMap()).containsEntry(AttributeKey.stringKey("service.namespace"), "my-group"); + }); + } + + @Test + void shouldNOtApplyServiceNamespaceIfApplicationGroupIsNotSet() { + this.runner.run((context -> { + Resource resource = context.getBean(Resource.class); + assertThat(resource.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("service.namespace")); + })); + } + @Test void shouldFallbackToDefaultApplicationNameIfSpringApplicationNameIsNotSet() { this.runner.run((context) -> { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java index 61527264b998..f19f74f82ec8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java @@ -156,9 +156,10 @@ void unknownServiceShouldBeUsedAsDefaultServiceName() { @Test void springApplicationGroupNameShouldBeUsedAsDefaultServiceGroup() { this.environment.setProperty("spring.application.group", "spring-boot"); - assertThat(getAttributes()).hasSize(2) + assertThat(getAttributes()).hasSize(3) .containsEntry("service.name", "unknown_service") - .containsEntry("service.group", "spring-boot"); + .containsEntry("service.group", "spring-boot") + .containsEntry("service.namespace", "spring-boot"); } @Test @@ -167,6 +168,11 @@ void springApplicationNameShouldBeUsedAsDefaultServiceName() { assertThat(getAttributes()).hasSize(1).containsEntry("service.name", "spring-boot-app"); } + @Test + void serviceNamespaceShouldNotBePresentByDefault() { + assertThat(getAttributes()).hasSize(1).doesNotContainKey("service.namespace"); + } + @Test void resourceAttributesShouldTakePrecedenceOverSpringApplicationName() { this.resourceAttributes.put("service.name", "spring-boot"); @@ -192,18 +198,50 @@ void otelServiceNameShouldTakePrecedenceOverSpringApplicationName() { void resourceAttributesShouldTakePrecedenceOverSpringApplicationGroupName() { this.resourceAttributes.put("service.group", "spring-boot-app"); this.environment.setProperty("spring.application.group", "spring-boot"); - assertThat(getAttributes()).hasSize(2) + assertThat(getAttributes()).hasSize(3) .containsEntry("service.name", "unknown_service") .containsEntry("service.group", "spring-boot-app"); } + @Test + void resourceAttributesShouldTakePrecedenceOverApplicationGroupNameForPopulatingServiceNamespace() { + this.resourceAttributes.put("service.namespace", "spring-boot-app"); + this.environment.setProperty("spring.application.group", "overriden"); + assertThat(getAttributes()).hasSize(3) + .containsEntry("service.name", "unknown_service") + .containsEntry("service.group", "overriden") + .containsEntry("service.namespace", "spring-boot-app"); + } + @Test void otelResourceAttributesShouldTakePrecedenceOverSpringApplicationGroupName() { this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "service.group=spring-boot"); this.environment.setProperty("spring.application.group", "spring-boot-app"); - assertThat(getAttributes()).hasSize(2) + assertThat(getAttributes()).hasSize(3) .containsEntry("service.name", "unknown_service") - .containsEntry("service.group", "spring-boot"); + .containsEntry("service.group", "spring-boot") + .containsEntry("service.namespace", "spring-boot-app"); + } + + @Test + void otelResourceAttributesShouldTakePrecedenceOverSpringApplicationGroupNameForServiceNamespace() { + this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "service.namespace=spring-boot"); + this.environment.setProperty("spring.application.group", "overriden"); + ; + assertThat(getAttributes()).hasSize(3) + .containsEntry("service.group", "overriden") + .containsEntry("service.namespace", "spring-boot"); + } + + @Test + void shouldUseServiceGroupForServiceNamespaceIfServiceGroupIsSet() { + this.environment.setProperty("spring.application.group", "alpha"); + assertThat(getAttributes()).containsEntry("service.namespace", "alpha"); + } + + @Test + void shouldNotSetServiceNamespaceIfServiceGroupIsNotSet() { + assertThat(getAttributes()).doesNotContainKey("service.namespace"); } private Map getAttributes() {