diff --git a/docs/src/main/asciidoc/project-features.adoc b/docs/src/main/asciidoc/project-features.adoc index 35d0433..42b9bed 100644 --- a/docs/src/main/asciidoc/project-features.adoc +++ b/docs/src/main/asciidoc/project-features.adoc @@ -41,6 +41,11 @@ We're providing an Slf4j integration via a `SpanProcessor` that injects to and r If it's there on the classpath, we integrate with the `LoggingSpanExporter`. You can disable that integration via the `spring.sleuth.otel.log.exporter.enabled=false` property. +[[features-otel-resource-customization]] +=== OpenTelemetry Resource Customization + +OpenTelemetry provides a `Resource` abstraction which captures identifying information about the entities for which signals (stats or traces) are reported. If you wish to customize that `Resource` you can register as a bean an implementation of the `ResourceCustomizer` interface. + [[features-otel-opentracing]] === OpenTelemetry Opentracing diff --git a/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java b/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java index cc9ef25..94d85af 100644 --- a/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java +++ b/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java @@ -100,12 +100,18 @@ SdkTracerProvider otelTracerProvider(SpanLimits spanLimits, ObjectProvider> resourceCustomizerProvider) { String applicationName = env.getProperty("spring.application.name"); + Resource resource = defaultResource(applicationName); + List resourceCustomizers = resourceCustomizerProvider.getIfAvailable(ArrayList::new); + for (ResourceCustomizer customizer : resourceCustomizers) { + Resource customized = customizer.customize(resource); + resource = resource.merge(customized); + } + return resource; + } + + private Resource defaultResource(String applicationName) { if (applicationName == null) { return Resource.getDefault(); } diff --git a/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/ResourceCustomizer.java b/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/ResourceCustomizer.java new file mode 100644 index 0000000..9ab10d5 --- /dev/null +++ b/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/ResourceCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.autoconfig.otel; + +import io.opentelemetry.sdk.resources.Resource; + +/** + * Allows to customize the {@link Resource}. + * + * @author Marcin Grzejszczak + * @since 1.0.0 + */ +@FunctionalInterface +public interface ResourceCustomizer { + + /** + * Customizes the resource or returns itself if there's nothing to customize. + * @param resource - resource to customize + * @return resource to be merged with the customized resource + */ + Resource customize(Resource resource); + +} diff --git a/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinOtelAutoConfiguration.java b/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinOtelAutoConfiguration.java index 52a5bff..ca508dc 100644 --- a/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinOtelAutoConfiguration.java +++ b/spring-cloud-sleuth-otel-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinOtelAutoConfiguration.java @@ -16,8 +16,11 @@ package org.springframework.cloud.sleuth.autoconfig.otel.zipkin2; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import zipkin2.reporter.Sender; import org.springframework.beans.factory.annotation.Qualifier; @@ -25,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.autoconfig.otel.ResourceCustomizer; import org.springframework.cloud.sleuth.autoconfig.zipkin2.ZipkinAutoConfiguration; import org.springframework.cloud.sleuth.zipkin2.DefaultZipkinRestTemplateCustomizer; import org.springframework.cloud.sleuth.zipkin2.EndpointLocator; @@ -62,11 +66,22 @@ static class ZipkinConfiguration { @Bean @ConditionalOnMissingBean ZipkinSpanExporter otelZipkinSpanExporter(ZipkinProperties zipkinProperties, - @Qualifier(ZipkinAutoConfiguration.SENDER_BEAN_NAME) Sender sender, Environment env) { + @Qualifier(ZipkinAutoConfiguration.SENDER_BEAN_NAME) Sender sender) { return ZipkinSpanExporter.builder().setEndpoint(zipkinProperties.getBaseUrl() + "api/v2/spans") .setSender(sender).setEncoder(zipkinProperties.getEncoder()).build(); } + @Bean + ResourceCustomizer zipkinResourceCustomizer(Environment environment) { + return resource -> { + String zipkinServiceName = environment.getProperty("spring.zipkin.service.name"); + if (zipkinServiceName == null) { + return resource; + } + return Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, zipkinServiceName)); + }; + } + } } diff --git a/spring-cloud-sleuth-otel-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinSamplerTests.java b/spring-cloud-sleuth-otel-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinSamplerTests.java index 6909f53..a045a7d 100644 --- a/spring-cloud-sleuth-otel-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinSamplerTests.java +++ b/spring-cloud-sleuth-otel-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/otel/zipkin2/ZipkinSamplerTests.java @@ -16,6 +16,8 @@ package org.springframework.cloud.sleuth.autoconfig.otel.zipkin2; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.resources.Resource; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; @@ -45,6 +47,19 @@ void should_set_sampler_to_non_off_when_zipkin_handler_on_classpath_for_otel() { }); } + @Test + void should_set_service_name_to_zipkin_service_name() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues("spring.sleuth.tracer.mode=AUTO", "spring.application.name=foo", + "spring.zipkin.service.name=bar") + .withConfiguration(AutoConfigurations.of(TestConfig.class)); + + contextRunner.run(context -> { + Resource resource = context.getBean(Resource.class); + BDDAssertions.then(resource.getAttributes().get(AttributeKey.stringKey("service.name"))).isEqualTo("bar"); + }); + } + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class TestConfig {