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 52c66a4485..c28a4446cb 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
@@ -86,7 +86,7 @@ public final class OpenTelemetryResourceAttributes {
*
* Additionally, {@code spring.application.name} or {@code unknown_service} will be
* used as the default for {@code service.name}, and {@code spring.application.group}
- * will serve as the default for {@code service.group}.
+ * will serve as the default for {@code service.group} and {@code service.namespace}.
* @param consumer the {@link BiConsumer} to apply
*/
public void applyTo(BiConsumer consumer) {
@@ -97,8 +97,9 @@ public final class OpenTelemetryResourceAttributes {
attributes.put(name, value);
}
});
- attributes.computeIfAbsent("service.name", (k) -> getApplicationName());
- attributes.computeIfAbsent("service.group", (k) -> getApplicationGroup());
+ attributes.computeIfAbsent("service.name", (key) -> getApplicationName());
+ attributes.computeIfAbsent("service.group", (key) -> getApplicationGroup());
+ attributes.computeIfAbsent("service.namespace", (key) -> getServiceNamespace());
attributes.forEach(consumer);
}
@@ -106,11 +107,21 @@ public final class OpenTelemetryResourceAttributes {
return this.environment.getProperty("spring.application.name", DEFAULT_SERVICE_NAME);
}
+ /**
+ * Returns the application group.
+ * @return the application group
+ * @deprecated since 3.5.0 for removal in 3.7.0
+ */
+ @Deprecated(since = "3.5.0", forRemoval = true)
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 14ab7b3c63..34492b1fee 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,23 @@ class OpenTelemetryAutoConfigurationTests {
});
}
+ @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) -> {
@@ -156,7 +173,7 @@ class OpenTelemetryAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
- private static final class UserConfiguration {
+ static class UserConfiguration {
@Bean
OpenTelemetry customOpenTelemetry() {
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 61527264b9..6712744e91 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 @@ class OpenTelemetryResourceAttributesTests {
@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 @@ class OpenTelemetryResourceAttributesTests {
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,49 @@ class OpenTelemetryResourceAttributesTests {
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() {