Spring Boot Starter service-name is constant
* Spring Boot Starter service-name is constant
Pattern-based resource configuration
* Add ResourceProvider beans for spring with ConfigProperties
MODIFY
instrumentation/spring/spring-boot-autoconfigure/README.md
instrumentation/spring/spring-boot-autoconfigure/README.md
@@ -392,6 +392,26 @@ If an exporter is present in the classpath during runtime and a spring bean of t
<!-- Slf4j Log Correlation otel.springboot.loggers.slf4j.enabled true org.slf4j.MDC -->
+##### Resource Properties
+
+| Feature | Property | Default Value |
+|----------|--------------------------------------------------|------------------------|
+| Resource | otel.springboot.resource.enabled | `true` |
+| | otel.springboot.resource.attributes.service.name | `unknown_service:java` |
+| | otel.springboot.resource.attributes | `empty map` |
+
+`unknown_service:java` will be used as the service-name if no value has been specified to the
+property `spring.application.name` or `otel.springboot.resource.attributes.service.name` (which has
+the highest priority)
+
+`otel.springboot.resource.attributes` supports a pattern-based resource configuration in the
+application.properties like this:
+
+```
+otel.springboot.resource.attributes.environment=dev
+otel.springboot.resource.attributes.xyz=foo
+```
+
##### Exporter Properties
| Feature | Property | Default Value |

MODIFY
instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts
instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts
@@ -22,9 +22,11 @@ dependencies {
compileOnly("org.springframework.boot:spring-boot-starter-web:${versions["org.springframework.boot"]}")
compileOnly("org.springframework.boot:spring-boot-starter-webflux:${versions["org.springframework.boot"]}")
+ compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
compileOnly("io.opentelemetry:opentelemetry-extension-annotations")
compileOnly("io.opentelemetry:opentelemetry-extension-trace-propagators")
compileOnly("io.opentelemetry:opentelemetry-extension-aws")
+ compileOnly("io.opentelemetry:opentelemetry-sdk-extension-resources")
compileOnly("io.opentelemetry:opentelemetry-exporter-logging")
compileOnly("io.opentelemetry:opentelemetry-exporter-jaeger")
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
@@ -40,6 +42,8 @@ dependencies {
testImplementation(project(":testing-common"))
testImplementation("io.opentelemetry:opentelemetry-sdk")
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-extension-resources")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators")
testImplementation("io.opentelemetry:opentelemetry-extension-aws")

MODIFY
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java
@@ -8,7 +8,11 @@ package io.opentelemetry.instrumentation.spring.autoconfigure;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators;
+import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringResourceConfigProperties;
import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
+import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
@@ -21,6 +25,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Create {@link io.opentelemetry.api.trace.Tracer} bean if bean is missing.
@@ -41,7 +47,8 @@ public class OpenTelemetryAutoConfiguration {
@ConditionalOnMissingBean
public SdkTracerProvider sdkTracerProvider(
SamplerProperties samplerProperties,
- ObjectProvider<List<SpanExporter>> spanExportersProvider) {
+ ObjectProvider<List<SpanExporter>> spanExportersProvider,
+ Resource otelResource) {
SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();
spanExportersProvider.getIfAvailable(Collections::emptyList).stream()
@@ -49,10 +56,24 @@ public class OpenTelemetryAutoConfiguration {
.forEach(tracerProviderBuilder::addSpanProcessor);
return tracerProviderBuilder
+ .setResource(otelResource)
.setSampler(Sampler.traceIdRatioBased(samplerProperties.getProbability()))
.build();
}
+ @Bean
+ @ConditionalOnMissingBean
+ public Resource otelResource(
+ Environment env, ObjectProvider<List<ResourceProvider>> resourceProviders) {
+ ConfigProperties config = new SpringResourceConfigProperties(env, new SpelExpressionParser());
+ Resource resource = Resource.getDefault();
+ for (ResourceProvider resourceProvider :
+ resourceProviders.getIfAvailable(Collections::emptyList)) {
+ resource = resource.merge(resourceProvider.createResource(config));
+ }
+ return resource;
+ }
+
@Bean
public OpenTelemetry openTelemetry(
ObjectProvider<ContextPropagators> propagatorsProvider, SdkTracerProvider tracerProvider) {

ADD
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfiguration.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
+import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
+import io.opentelemetry.sdk.extension.resources.ContainerResource;
+import io.opentelemetry.sdk.extension.resources.ContainerResourceProvider;
+import io.opentelemetry.sdk.extension.resources.HostResource;
+import io.opentelemetry.sdk.extension.resources.HostResourceProvider;
+import io.opentelemetry.sdk.extension.resources.OsResource;
+import io.opentelemetry.sdk.extension.resources.OsResourceProvider;
+import io.opentelemetry.sdk.extension.resources.ProcessResource;
+import io.opentelemetry.sdk.extension.resources.ProcessResourceProvider;
+import io.opentelemetry.sdk.extension.resources.ProcessRuntimeResource;
+import io.opentelemetry.sdk.extension.resources.ProcessRuntimeResourceProvider;
+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.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableConfigurationProperties(OtelResourceProperties.class)
+@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
+@ConditionalOnProperty(prefix = "otel.springboot.resource", name = "enabled", matchIfMissing = true)
+public class OtelResourceAutoConfiguration {
+
+ @Bean
+ public ResourceProvider otelResourceProvider(OtelResourceProperties otelResourceProperties) {
+ return new SpringResourceProvider(otelResourceProperties);
+ }
+
+ @Bean
+ @ConditionalOnClass(OsResource.class)
+ public ResourceProvider otelOsResourceProvider() {
+ return new OsResourceProvider();
+ }
+
+ @Bean
+ @ConditionalOnClass(ProcessResource.class)
+ public ResourceProvider otelProcessResourceProvider() {
+ return new ProcessResourceProvider();
+ }
+
+ @Bean
+ @ConditionalOnClass(ProcessRuntimeResource.class)
+ public ResourceProvider otelProcessRuntimeResourceProvider() {
+ return new ProcessRuntimeResourceProvider();
+ }
+
+ @Bean
+ @ConditionalOnClass(HostResource.class)
+ public ResourceProvider otelHostResourceProvider() {
+ return new HostResourceProvider();
+ }
+
+ @Bean
+ @ConditionalOnClass(ContainerResource.class)
+ public ResourceProvider otelContainerResourceProvider() {
+ return new ContainerResourceProvider();
+ }
+}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceProperties.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+import java.util.Collections;
+import java.util.Map;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "otel.springboot.resource")
+public class OtelResourceProperties {
+ private Map<String, String> attributes = Collections.emptyMap();
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceConfigProperties.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import javax.annotation.Nullable;
+import org.springframework.core.env.Environment;
+import org.springframework.expression.ExpressionParser;
+
+public class SpringResourceConfigProperties implements ConfigProperties {
+ private final Environment environment;
+
+ private final ExpressionParser parser;
+
+ public SpringResourceConfigProperties(Environment environment, ExpressionParser parser) {
+ this.environment = environment;
+ this.parser = parser;
+ }
+
+ @Nullable
+ @Override
+ public String getString(String name) {
+ return environment.getProperty(name, String.class);
+ }
+
+ @Nullable
+ @Override
+ public Boolean getBoolean(String name) {
+ return environment.getProperty(name, Boolean.class);
+ }
+
+ @Nullable
+ @Override
+ public Integer getInt(String name) {
+ return environment.getProperty(name, Integer.class);
+ }
+
+ @Nullable
+ @Override
+ public Long getLong(String name) {
+ return environment.getProperty(name, Long.class);
+ }
+
+ @Nullable
+ @Override
+ public Double getDouble(String name) {
+ return environment.getProperty(name, Double.class);
+ }
+
+ @Nullable
+ @Override
+ public Duration getDuration(String name) {
+ return environment.getProperty(name, Duration.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<String> getList(String name) {
+ return (List<String>) environment.getProperty(name, List.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map<String, String> getMap(String name) {
+ String value = environment.getProperty(name);
+ return (Map<String, String>) parser.parseExpression(Objects.requireNonNull(value)).getValue();
+ }
+}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceProvider.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+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.ResourceProvider;
+import io.opentelemetry.sdk.resources.Resource;
+import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
+import java.util.Map;
+
+public class SpringResourceProvider implements ResourceProvider {
+
+ private final OtelResourceProperties otelResourceProperties;
+
+ public SpringResourceProvider(OtelResourceProperties otelResourceProperties) {
+ this.otelResourceProperties = otelResourceProperties;
+ }
+
+ @Override
+ public Resource createResource(ConfigProperties configProperties) {
+ String applicationName = configProperties.getString("spring.application.name");
+ Map<String, String> attributes = otelResourceProperties.getAttributes();
+ AttributesBuilder attributesBuilder = Attributes.builder();
+ attributes.forEach(attributesBuilder::put);
+ return defaultResource(applicationName).merge(Resource.create(attributesBuilder.build()));
+ }
+
+ private static Resource defaultResource(String applicationName) {
+ if (applicationName == null) {
+ return Resource.getDefault();
+ }
+ return Resource.getDefault()
+ .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
+ }
+}

MODIFY
instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -8,4 +8,5 @@ io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfigura
io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate.RestTemplateAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.webclient.WebClientAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.webmvc.WebMvcFilterAutoConfiguration,\
-io.opentelemetry.instrumentation.spring.autoconfigure.aspects.TraceAspectAutoConfiguration
+io.opentelemetry.instrumentation.spring.autoconfigure.aspects.TraceAspectAutoConfiguration,\
+io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration

MODIFY
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfigurationTest.java
package io.opentelemetry.instrumentation.spring.autoconfigure;
+import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration;
+import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -69,4 +73,76 @@ class OpenTelemetryAutoConfigurationTest {
.hasBean("customTracerProvider")
.doesNotHaveBean("sdkTracerProvider"));
}
+
+ @Test
+ @DisplayName(
+ "when spring.application.name is set value should be passed to service name attribute")
+ void shouldDetermineServiceNameBySpringApplicationName() {
+ this.contextRunner
+ .withPropertyValues("spring.application.name=myapp-backend")
+ .withConfiguration(
+ AutoConfigurations.of(
+ OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class))
+ .run(
+ context -> {
+ Resource otelResource = context.getBean("otelResource", Resource.class);
+
+ assertThat(otelResource.getAttribute(SERVICE_NAME)).isEqualTo("myapp-backend");
+ });
+ }
+
+ @Test
+ @DisplayName(
+ "when spring application name and otel service name are not set service name should be default")
+ void hasDefaultServiceName() {
+ this.contextRunner
+ .withConfiguration(
+ AutoConfigurations.of(
+ OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class))
+ .run(
+ context -> {
+ Resource otelResource = context.getBean("otelResource", Resource.class);
+
+ assertThat(otelResource.getAttribute(SERVICE_NAME)).isEqualTo("unknown_service:java");
+ });
+ }
+
+ @Test
+ @DisplayName("when otel service name is set it should be set as service name attribute")
+ void shouldDetermineServiceNameByOtelServiceName() {
+ this.contextRunner
+ .withConfiguration(
+ AutoConfigurations.of(
+ OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class))
+ .withPropertyValues("otel.springboot.resource.attributes.service.name=otel-name-backend")
+ .run(
+ context -> {
+ Resource otelResource = context.getBean("otelResource", Resource.class);
+
+ assertThat(otelResource.getAttribute(SERVICE_NAME)).isEqualTo("otel-name-backend");
+ });
+ }
+
+ @Test
+ @DisplayName("when otel attributes are set in properties they should be put in resource")
+ void shouldInitializeAttributes() {
+ this.contextRunner
+ .withConfiguration(
+ AutoConfigurations.of(
+ OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class))
+ .withPropertyValues(
+ "otel.springboot.resource.attributes.xyz=foo",
+ "otel.springboot.resource.attributes.environment=dev",
+ "otel.springboot.resource.attributes.service.instance.id=id-example")
+ .run(
+ context -> {
+ Resource otelResource = context.getBean("otelResource", Resource.class);
+
+ assertThat(otelResource.getAttribute(AttributeKey.stringKey("environment")))
+ .isEqualTo("dev");
+ assertThat(otelResource.getAttribute(AttributeKey.stringKey("xyz"))).isEqualTo("foo");
+ assertThat(otelResource.getAttribute(AttributeKey.stringKey("service.instance.id")))
+ .isEqualTo("id-example");
+ });
+ }
}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourceAutoConfigurationTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+public class OtelResourceAutoConfigurationTest {
+ private final ApplicationContextRunner contextRunner =
+ new ApplicationContextRunner()
+ .withConfiguration(
+ AutoConfigurations.of(
+ OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class));
+
+ @Test
+ @DisplayName(
+ "when otel.springboot.resource.enabled is set to true configuration should be initialized")
+ void shouldDetermineServiceNameByOtelServiceName() {
+ this.contextRunner
+ .withPropertyValues("otel.springboot.resource.enabled=true")
+ .run(context -> assertThat(context.containsBean("otelResourceProvider")).isTrue());
+ }
+
+ @Test
+ @DisplayName(
+ "when otel.springboot.resource.enabled is not specified configuration should be initialized")
+ void shouldInitAutoConfigurationByDefault() {
+ this.contextRunner.run(
+ context -> assertThat(context.containsBean("otelResourceProvider")).isTrue());
+ }
+
+ @Test
+ @DisplayName(
+ "when otel.springboot.resource.enabled is set to false configuration should NOT be initialized")
+ void shouldNotInitAutoConfiguration() {
+ this.contextRunner
+ .withPropertyValues("otel.springboot.resource.enabled=false")
+ .run(context -> assertThat(context.containsBean("otelResourceProvider")).isFalse());
+ }
+}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/OtelResourcePropertiesTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+public class OtelResourcePropertiesTest {
+ private final ApplicationContextRunner contextRunner =
+ new ApplicationContextRunner()
+ .withPropertyValues("otel.springboot.resource.enabled=true")
+ .withConfiguration(AutoConfigurations.of(OtelResourceAutoConfiguration.class));
+
+ @Test
+ @DisplayName("when attributes are SET should set OtelResourceProperties with given attributes")
+ void hasAttributes() {
+
+ this.contextRunner
+ .withPropertyValues(
+ "otel.springboot.resource.attributes.environment=dev",
+ "otel.springboot.resource.attributes.xyz=foo",
+ "otel.springboot.resource.attributes.service.name=backend-name",
+ "otel.springboot.resource.attributes.service.instance.id=id-example")
+ .run(
+ context -> {
+ OtelResourceProperties propertiesBean = context.getBean(OtelResourceProperties.class);
+
+ assertThat(propertiesBean.getAttributes())
+ .contains(
+ entry("environment", "dev"),
+ entry("xyz", "foo"),
+ entry("service.name", "backend-name"),
+ entry("service.instance.id", "id-example"));
+ });
+ }
+
+ @Test
+ @DisplayName("when attributes are DEFAULT should set OtelResourceProperties to default values")
+ void hasDefaultTypes() {
+
+ this.contextRunner.run(
+ context ->
+ assertThat(context.getBean(OtelResourceProperties.class).getAttributes()).isEmpty());
+ }
+}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/resources/SpringResourceConfigPropertiesTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.core.env.Environment;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+
+class SpringResourceConfigPropertiesTest {
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
+
+ @Test
+ @DisplayName("when map is set in properties in a row it should be available in config")
+ void shouldInitializeAttributesByMapInArow() {
+ this.contextRunner
+ .withPropertyValues(
+ "otel.springboot.test.map={'environment':'dev','xyz':'foo','service.instance.id':'id-example'}")
+ .run(
+ context -> {
+ Environment env = context.getBean("environment", Environment.class);
+ SpringResourceConfigProperties config =
+ new SpringResourceConfigProperties(env, new SpelExpressionParser());
+
+ assertThat(config.getMap("otel.springboot.test.map"))
+ .contains(
+ entry("environment", "dev"),
+ entry("xyz", "foo"),
+ entry("service.instance.id", "id-example"));
+ });
+ }
+}

Spring Boot Starter service-name is constant (#5359)

* Spring Boot Starter service-name is constant

Pattern-based resource configuration

* Add ResourceProvider beans for spring with ConfigProperties