Skip to content

Commit

Permalink
Spring Boot Starter service-name is constant (open-telemetry#5359)
Browse files Browse the repository at this point in the history
* Spring Boot Starter service-name is constant

Pattern-based resource configuration

* Add ResourceProvider beans for spring with ConfigProperties
  • Loading branch information
aschugunov authored and RashmiRam committed May 23, 2022
1 parent 4d250a8 commit fe00545
Show file tree
Hide file tree
Showing 12 changed files with 469 additions and 2 deletions.
20 changes: 20 additions & 0 deletions instrumentation/spring/spring-boot-autoconfigure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
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;
Expand All @@ -21,6 +25,8 @@
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 All @@ -41,18 +47,33 @@ public static class OpenTelemetryBeanConfig {
@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()
.map(spanExporter -> BatchSpanProcessor.builder(spanExporter).build())
.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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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;
}
}
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 @@ -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
Loading

0 comments on commit fe00545

Please sign in to comment.