Skip to content

Commit

Permalink
Add ResourceProvider beans for spring with ConfigProperties
Browse files Browse the repository at this point in the history
  • Loading branch information
aschugunov committed Mar 29, 2022
1 parent 53678f2 commit 393e0b9
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ 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")
Expand All @@ -42,6 +43,7 @@ dependencies {
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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,27 @@
package io.opentelemetry.instrumentation.spring.autoconfigure;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
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;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.springframework.beans.factory.ObjectProvider;
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.
Expand Down Expand Up @@ -63,25 +64,16 @@ public SdkTracerProvider sdkTracerProvider(
@Bean
@ConditionalOnMissingBean
public Resource otelResource(
Environment env, ObjectProvider<List<Supplier<Resource>>> resourceProviders) {
String applicationName = env.getProperty("spring.application.name");
Resource resource = defaultResource(applicationName);
List<Supplier<Resource>> resourceCustomizers =
resourceProviders.getIfAvailable(Collections::emptyList);
for (Supplier<Resource> resourceCustomizer : resourceCustomizers) {
resource = resource.merge(resourceCustomizer.get());
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;
}

private static Resource defaultResource(String applicationName) {
if (applicationName == null) {
return Resource.getDefault();
}
return Resource.getDefault()
.merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
}

@Bean
public OpenTelemetry openTelemetry(
ObjectProvider<ContextPropagators> propagatorsProvider, SdkTracerProvider tracerProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure;
package io.opentelemetry.instrumentation.spring.autoconfigure.resources;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
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.resources.Resource;
import java.util.Map;
import java.util.function.Supplier;
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;
Expand All @@ -29,44 +31,37 @@
public class OtelResourceAutoConfiguration {

@Bean
public Supplier<Resource> otelResourceProvider(OtelResourceProperties otelResourceProperties) {
return () -> {
AttributesBuilder attributesBuilder = Attributes.builder();
for (Map.Entry<String, String> entry : otelResourceProperties.getAttributes().entrySet()) {
attributesBuilder.put(entry.getKey(), entry.getValue());
}
Attributes attributes = attributesBuilder.build();
return Resource.create(attributes);
};
public ResourceProvider otelResourceProvider(OtelResourceProperties otelResourceProperties) {
return new SpringResourceProvider(otelResourceProperties);
}

@Bean
@ConditionalOnClass(OsResource.class)
public Supplier<Resource> otelOsResourceProvider() {
return OsResource::get;
public ResourceProvider otelOsResourceProvider() {
return new OsResourceProvider();
}

@Bean
@ConditionalOnClass(ProcessResource.class)
public Supplier<Resource> otelProcessResourceProvider() {
return ProcessResource::get;
public ResourceProvider otelProcessResourceProvider() {
return new ProcessResourceProvider();
}

@Bean
@ConditionalOnClass(ProcessRuntimeResource.class)
public Supplier<Resource> otelProcessRuntimeResourceProvider() {
return ProcessRuntimeResource::get;
public ResourceProvider otelProcessRuntimeResourceProvider() {
return new ProcessRuntimeResourceProvider();
}

@Bean
@ConditionalOnClass(HostResource.class)
public Supplier<Resource> otelHostResourceProvider() {
return HostResource::get;
public ResourceProvider otelHostResourceProvider() {
return new HostResourceProvider();
}

@Bean
@ConditionalOnClass(ContainerResource.class)
public Supplier<Resource> otelContainerResource() {
return ContainerResource::get;
public ResourceProvider otelContainerResourceProvider() {
return new ContainerResourceProvider();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure;
package io.opentelemetry.instrumentation.spring.autoconfigure.resources;

import java.util.Collections;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate.R
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.OtelResourceAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
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;
Expand Down Expand Up @@ -78,7 +80,9 @@ void initializeOpenTelemetry() {
void shouldDetermineServiceNameBySpringApplicationName() {
this.contextRunner
.withPropertyValues("spring.application.name=myapp-backend")
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
.withConfiguration(
AutoConfigurations.of(
OtelResourceAutoConfiguration.class, OpenTelemetryAutoConfiguration.class))
.run(
context -> {
Resource otelResource = context.getBean("otelResource", Resource.class);
Expand All @@ -92,12 +96,53 @@ void shouldDetermineServiceNameBySpringApplicationName() {
"when spring application name and otel service name are not set service name should be default")
void hasDefaultServiceName() {
this.contextRunner
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
.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");
});
}
}
Loading

0 comments on commit 393e0b9

Please sign in to comment.