re-use sdk logic for configuring otlp exporters (#10292)

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Gregor Zeitlinger 2024-02-06 05:44:37 +01:00 committed by GitHub
parent d4435c9bc8
commit f715a189bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 599 additions and 623 deletions

View File

@ -8,12 +8,13 @@ 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.exporters.otlp.OtlpLoggerExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpExporterProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLogRecordExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.MapConverter;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringResourceConfigProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringConfigProperties;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
@ -55,7 +56,11 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
* <p>Updates the sampler probability for the configured {@link TracerProvider}.
*/
@Configuration
@EnableConfigurationProperties({MetricExportProperties.class, SamplerProperties.class})
@EnableConfigurationProperties({
MetricExportProperties.class,
SamplerProperties.class,
OtlpExporterProperties.class
})
public class OpenTelemetryAutoConfiguration {
public OpenTelemetryAutoConfiguration() {}
@ -81,7 +86,7 @@ public class OpenTelemetryAutoConfiguration {
@ConditionalOnBean(OtelResourceAutoConfiguration.class)
static class Resource {}
@ConditionalOnBean(OtlpLoggerExporterAutoConfiguration.class)
@ConditionalOnBean(OtlpLogRecordExporterAutoConfiguration.class)
static class Logger {}
@ConditionalOnBean(OtlpSpanExporterAutoConfiguration.class)
@ -91,6 +96,13 @@ public class OpenTelemetryAutoConfiguration {
static class Metric {}
}
@Bean
@ConditionalOnMissingBean
ConfigProperties configProperties(
Environment env, OtlpExporterProperties otlpExporterProperties) {
return new SpringConfigProperties(env, new SpelExpressionParser(), otlpExporterProperties);
}
@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean
public SdkTracerProvider sdkTracerProvider(
@ -156,8 +168,7 @@ public class OpenTelemetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Resource otelResource(
Environment env, ObjectProvider<List<ResourceProvider>> resourceProviders) {
ConfigProperties config = new SpringResourceConfigProperties(env, new SpelExpressionParser());
ConfigProperties config, ObjectProvider<List<ResourceProvider>> resourceProviders) {
Resource resource = Resource.getDefault();
for (ResourceProvider resourceProvider :
resourceProviders.getIfAvailable(Collections::emptyList)) {

View File

@ -1,49 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration for {@link io.opentelemetry.exporter.logging.LoggingSpanExporter} and {@link
* io.opentelemetry.exporter.logging.LoggingMetricExporter}.
*/
@ConfigurationProperties(prefix = "otel.exporter.logging")
public final class LoggingExporterProperties {
private boolean enabled = false;
private final SignalProperties traces = new SignalProperties();
private final SignalProperties metrics = new SignalProperties();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public SignalProperties getTraces() {
return traces;
}
public SignalProperties getMetrics() {
return metrics;
}
public static class SignalProperties {
private boolean enabled = false;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@ -11,7 +11,6 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.Condition;
import org.springframework.context.annotation.Conditional;
@ -19,7 +18,6 @@ import org.springframework.context.annotation.Configuration;
/** Configures {@link LoggingMetricExporter} bean for tracing. */
@Configuration
@EnableConfigurationProperties(LoggingExporterProperties.class)
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@Conditional(LoggingMetricExporterAutoConfiguration.CustomCondition.class)
@ConditionalOnClass(LoggingMetricExporter.class)

View File

@ -11,7 +11,6 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.Condition;
import org.springframework.context.annotation.Conditional;
@ -19,7 +18,6 @@ import org.springframework.context.annotation.Configuration;
/** Configures {@link LoggingSpanExporter} bean for tracing. */
@Configuration
@EnableConfigurationProperties(LoggingExporterProperties.class)
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@Conditional(LoggingSpanExporterAutoConfiguration.CustomCondition.class)
@ConditionalOnClass(LoggingSpanExporter.class)

View File

@ -5,76 +5,24 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter} and {@link
* io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter}.
*
* <p>Get Exporter Service Name
*
* <p>Get Exporter Endpoint
*
* <p>Get max wait time for Collector to process Span Batches
*/
/** Configuration for OLTP exporters. */
@ConfigurationProperties(prefix = "otel.exporter.otlp")
public final class OtlpExporterProperties {
private boolean enabled = true;
@Nullable private String endpoint;
@Nullable private String protocol;
private final Map<String, String> headers = new HashMap<>();
@Nullable private Duration timeout;
private final SignalProperties traces = new SignalProperties();
private final SignalProperties metrics = new SignalProperties();
private final SignalProperties logs = new SignalProperties();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
@Nullable
public String getProtocol() {
return protocol;
}
public void setProtocol(@Nullable String protocol) {
this.protocol = protocol;
}
public Map<String, String> getHeaders() {
return headers;
}
@Nullable
public Duration getTimeout() {
return timeout;
}
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
public SignalProperties getTraces() {
return traces;
}
@ -88,53 +36,10 @@ public final class OtlpExporterProperties {
}
public static class SignalProperties {
private boolean enabled = true;
@Nullable private String endpoint;
@Nullable private String protocol;
private final Map<String, String> headers = new HashMap<>();
@Nullable private Duration timeout;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(@Nullable String endpoint) {
this.endpoint = endpoint;
}
@Nullable
public String getProtocol() {
return protocol;
}
public void setProtocol(@Nullable String protocol) {
this.protocol = protocol;
}
public Map<String, String> getHeaders() {
return headers;
}
@Nullable
public Duration getTimeout() {
return timeout;
}
public void setTimeout(@Nullable Duration timeout) {
this.timeout = timeout;
}
}
}

View File

@ -1,119 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import java.time.Duration;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class OtlpExporterUtil {
private OtlpExporterUtil() {}
private static final Logger logger = LoggerFactory.getLogger(OtlpExporterUtil.class);
static <G, H, E> E applySignalProperties(
String dataType,
OtlpExporterProperties properties,
OtlpExporterProperties.SignalProperties signalProperties,
Supplier<G> newGrpcBuilder,
Supplier<H> newHttpBuilder,
BiConsumer<G, String> setGrpcEndpoint,
BiConsumer<H, String> setHttpEndpoint,
BiConsumer<G, Map.Entry<String, String>> addGrpcHeader,
BiConsumer<H, Map.Entry<String, String>> addHttpHeader,
BiConsumer<G, Duration> setGrpcTimeout,
BiConsumer<H, Duration> setHttpTimeout,
Function<G, E> buildGrpcExporter,
Function<H, E> buildHttpExporter) {
String protocol = signalProperties.getProtocol();
if (protocol == null) {
protocol = properties.getProtocol();
}
G grpcBuilder = newGrpcBuilder.get();
H httpBuilder = newHttpBuilder.get();
boolean isHttpProtobuf = !"grpc".equals(protocol);
if (protocol != null
&& !OtlpConfigUtil.PROTOCOL_GRPC.equals(protocol)
&& !OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF.equals(protocol)) {
logger.warn(
"Unknown OTLP protocol '"
+ protocol
+ "', using '"
+ OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF
+ "'.");
}
String endpoint = signalProperties.getEndpoint();
if (endpoint == null) {
endpoint = properties.getEndpoint();
if (endpoint != null && isHttpProtobuf) {
if (!endpoint.endsWith("/")) {
endpoint += "/";
}
endpoint += signalPath(dataType);
}
}
if (endpoint != null) {
if (isHttpProtobuf) {
setHttpEndpoint.accept(httpBuilder, endpoint);
} else {
setGrpcEndpoint.accept(grpcBuilder, endpoint);
}
}
Map<String, String> headers = signalProperties.getHeaders();
if (headers.isEmpty()) {
headers = properties.getHeaders();
}
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (isHttpProtobuf) {
addHttpHeader.accept(httpBuilder, entry);
} else {
addGrpcHeader.accept(grpcBuilder, entry);
}
}
Duration timeout = signalProperties.getTimeout();
if (timeout == null) {
timeout = properties.getTimeout();
}
if (timeout != null) {
if (isHttpProtobuf) {
setHttpTimeout.accept(httpBuilder, timeout);
} else {
setGrpcTimeout.accept(grpcBuilder, timeout);
}
}
return isHttpProtobuf
? buildHttpExporter.apply(httpBuilder)
: buildGrpcExporter.apply(grpcBuilder);
}
private static String signalPath(String dataType) {
switch (dataType) {
case OtlpConfigUtil.DATA_TYPE_METRICS:
return "v1/metrics";
case OtlpConfigUtil.DATA_TYPE_TRACES:
return "v1/traces";
case OtlpConfigUtil.DATA_TYPE_LOGS:
return "v1/logs";
default:
throw new IllegalArgumentException(
"Cannot determine signal path for unrecognized data type: " + dataType);
}
}
}

View File

@ -6,49 +6,28 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import io.opentelemetry.exporter.otlp.internal.OtlpLogRecordExporterProvider;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.ExporterConfigEvaluator;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.Condition;
import org.springframework.context.annotation.Conditional;
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@EnableConfigurationProperties(OtlpExporterProperties.class)
@Conditional(OtlpLoggerExporterAutoConfiguration.CustomCondition.class)
@Conditional(OtlpLogRecordExporterAutoConfiguration.CustomCondition.class)
@ConditionalOnClass(OtlpGrpcLogRecordExporter.class)
public class OtlpLoggerExporterAutoConfiguration {
public class OtlpLogRecordExporterAutoConfiguration {
@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean({OtlpGrpcLogRecordExporter.class, OtlpHttpLogRecordExporter.class})
public LogRecordExporter otelOtlpLogRecordExporter(OtlpExporterProperties properties) {
return OtlpExporterUtil.applySignalProperties(
OtlpConfigUtil.DATA_TYPE_LOGS,
properties,
properties.getLogs(),
OtlpGrpcLogRecordExporter::builder,
OtlpHttpLogRecordExporter::builder,
OtlpGrpcLogRecordExporterBuilder::setEndpoint,
OtlpHttpLogRecordExporterBuilder::setEndpoint,
(builder, entry) -> {
builder.addHeader(entry.getKey(), entry.getValue());
},
(builder, entry) -> {
builder.addHeader(entry.getKey(), entry.getValue());
},
OtlpGrpcLogRecordExporterBuilder::setTimeout,
OtlpHttpLogRecordExporterBuilder::setTimeout,
OtlpGrpcLogRecordExporterBuilder::build,
OtlpHttpLogRecordExporterBuilder::build);
public LogRecordExporter otelOtlpLogRecordExporter(ConfigProperties configProperties) {
return new OtlpLogRecordExporterProvider().createExporter(configProperties);
}
static final class CustomCondition implements Condition {

View File

@ -6,17 +6,15 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import io.opentelemetry.exporter.otlp.internal.OtlpMetricExporterProvider;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.ExporterConfigEvaluator;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.Condition;
import org.springframework.context.annotation.Conditional;
@ -24,32 +22,14 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@EnableConfigurationProperties(OtlpExporterProperties.class)
@Conditional(OtlpMetricExporterAutoConfiguration.CustomCondition.class)
@ConditionalOnClass(OtlpGrpcMetricExporter.class)
public class OtlpMetricExporterAutoConfiguration {
@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean({OtlpGrpcMetricExporter.class, OtlpHttpMetricExporter.class})
public MetricExporter otelOtlpMetricExporter(OtlpExporterProperties properties) {
return OtlpExporterUtil.applySignalProperties(
OtlpConfigUtil.DATA_TYPE_METRICS,
properties,
properties.getLogs(),
OtlpGrpcMetricExporter::builder,
OtlpHttpMetricExporter::builder,
OtlpGrpcMetricExporterBuilder::setEndpoint,
OtlpHttpMetricExporterBuilder::setEndpoint,
(builder, entry) -> {
builder.addHeader(entry.getKey(), entry.getValue());
},
(builder, entry) -> {
builder.addHeader(entry.getKey(), entry.getValue());
},
OtlpGrpcMetricExporterBuilder::setTimeout,
OtlpHttpMetricExporterBuilder::setTimeout,
OtlpGrpcMetricExporterBuilder::build,
OtlpHttpMetricExporterBuilder::build);
public MetricExporter otelOtlpMetricExporter(ConfigProperties configProperties) {
return new OtlpMetricExporterProvider().createExporter(configProperties);
}
static final class CustomCondition implements Condition {

View File

@ -6,17 +6,15 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import io.opentelemetry.exporter.otlp.internal.OtlpSpanExporterProvider;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.internal.ExporterConfigEvaluator;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.Condition;
import org.springframework.context.annotation.Conditional;
@ -29,40 +27,14 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@EnableConfigurationProperties(OtlpExporterProperties.class)
@Conditional(OtlpSpanExporterAutoConfiguration.CustomCondition.class)
@ConditionalOnClass(OtlpGrpcSpanExporter.class)
public class OtlpSpanExporterAutoConfiguration {
@Bean
@ConditionalOnMissingBean({OtlpHttpSpanExporterBuilder.class})
public OtlpHttpSpanExporterBuilder otelOtlpHttpSpanExporterBuilder() {
// used for testing only - the builder is final
return OtlpHttpSpanExporter.builder();
}
@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean({OtlpGrpcSpanExporter.class, OtlpHttpSpanExporter.class})
public SpanExporter otelOtlpSpanExporter(
OtlpExporterProperties properties, OtlpHttpSpanExporterBuilder otlpHttpSpanExporterBuilder) {
return OtlpExporterUtil.applySignalProperties(
OtlpConfigUtil.DATA_TYPE_TRACES,
properties,
properties.getLogs(),
OtlpGrpcSpanExporter::builder,
() -> otlpHttpSpanExporterBuilder,
OtlpGrpcSpanExporterBuilder::setEndpoint,
OtlpHttpSpanExporterBuilder::setEndpoint,
(builder, entry) -> {
builder.addHeader(entry.getKey(), entry.getValue());
},
(builder, entry) -> {
builder.addHeader(entry.getKey(), entry.getValue());
},
OtlpGrpcSpanExporterBuilder::setTimeout,
OtlpHttpSpanExporterBuilder::setTimeout,
OtlpGrpcSpanExporterBuilder::build,
OtlpHttpSpanExporterBuilder::build);
public SpanExporter otelOtlpSpanExporter(ConfigProperties configProperties) {
return new OtlpSpanExporterProvider().createExporter(configProperties);
}
static final class CustomCondition implements Condition {

View File

@ -16,17 +16,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "otel.exporter.zipkin")
public class ZipkinSpanExporterProperties {
private boolean enabled = true;
@Nullable private String endpoint;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
public String getEndpoint() {
return endpoint;

View File

@ -0,0 +1,111 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpExporterProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.springframework.core.env.Environment;
import org.springframework.expression.ExpressionParser;
public class SpringConfigProperties implements ConfigProperties {
private final Environment environment;
private final ExpressionParser parser;
private final OtlpExporterProperties otlpExporterProperties;
public SpringConfigProperties(
Environment environment,
ExpressionParser parser,
OtlpExporterProperties otlpExporterProperties) {
this.environment = environment;
this.parser = parser;
this.otlpExporterProperties = otlpExporterProperties;
}
@Nullable
@Override
public String getString(String name) {
String value = environment.getProperty(name, String.class);
if (value == null && name.equals("otel.exporter.otlp.protocol")) {
// SDK autoconfigure module defaults to `grpc`, but this module aligns with recommendation
// in specification to default to `http/protobuf`
return OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF;
}
return value;
}
@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);
}
@SuppressWarnings("unchecked")
@Override
public List<String> getList(String name) {
return (List<String>) environment.getProperty(name, List.class);
}
@Nullable
@Override
public Duration getDuration(String name) {
String value = getString(name);
if (value == null) {
return null;
}
return DefaultConfigProperties.createFromMap(Collections.singletonMap(name, value))
.getDuration(name);
}
@SuppressWarnings("unchecked")
@Override
public Map<String, String> getMap(String name) {
// maps from config properties are not supported by Environment, so we have to fake it
switch (name) {
case "otel.exporter.otlp.headers":
return otlpExporterProperties.getHeaders();
case "otel.exporter.otlp.logs.headers":
return otlpExporterProperties.getLogs().getHeaders();
case "otel.exporter.otlp.metrics.headers":
return otlpExporterProperties.getMetrics().getHeaders();
case "otel.exporter.otlp.traces.headers":
return otlpExporterProperties.getTraces().getHeaders();
default:
break;
}
String value = environment.getProperty(name);
if (value == null) {
return Collections.emptyMap();
}
return (Map<String, String>) parser.parseExpression(value).getValue();
}
}

View File

@ -1,75 +0,0 @@
/*
* 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();
}
}

View File

@ -5,6 +5,198 @@
}
],
"properties": [
{
"name": "otel.experimental.exporter.otlp.retry.enabled",
"type": "java.lang.Boolean",
"description": "Enable experimental retry support. See https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#otlp-exporter-retry.",
"defaultValue": false
},
{
"name": "otel.exporter.otlp.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP trace, metric, or log server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default the host platform's trusted root certificates are used."
},
{
"name": "otel.exporter.otlp.client.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP trace, metric, or log client's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default, no chain file is used."
},
{
"name": "otel.exporter.otlp.client.key",
"type": "java.lang.String",
"description": "The path to the file containing private client key to use when verifying an OTLP trace, metric, or log client's TLS credentials.<br/>The file should contain one private key PKCS8 PEM format.<br/>By default, no client key is used."
},
{
"name": "otel.exporter.otlp.compression",
"type": "java.lang.String",
"description": "The compression type to use on OTLP trace, metric, and log requests.<br/>Options include <code>gzip</code>.<br/>By default, no compression will be used."
},
{
"name": "otel.exporter.otlp.endpoint",
"type": "java.lang.String",
"description": "The OTLP traces, metrics, and logs endpoint to connect to.<br/>Must be a URL with a scheme of either <code>http</code> or <code>https</code> based on the use of TLS. If protocol is <code>http/protobuf</code> the version and signal will be appended to the path (e.g. <code>v1/traces</code>, <code>v1/metrics</code>, or <code>v1/logs</code>).<br/>Default is <code>http://localhost:4317</code> when protocol is <code>grpc</code>, and <code>http://localhost:4318/v1/{signal}</code> when protocol is <code>http/protobuf</code>."
},
{
"name": "otel.exporter.otlp.headers",
"type": "java.util.Map<java.lang.String, java.lang.String>",
"description": "Request headers for OTLP trace, metric, and log requests.<br/>Can be either a Spring map or a key-value separated String, e.g. <code>key1=value1,key2=value2</code>."
},
{
"name": "otel.exporter.otlp.logs.certificate",
"type": "java.lang.String",
"description": " The path to the file containing trusted certificates to use when verifying an OTLP log server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default, the host platform's trusted root certificates are used."
},
{
"name": "otel.exporter.otlp.logs.client.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP log server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default, no chain file is used."
},
{
"name": "otel.exporter.otlp.logs.client.key",
"type": "java.lang.String",
"description": "The path to the file containing private client key to use when verifying an OTLP log client's TLS credentials.<br/>The file should contain one private key PKCS8 PEM format.<br/>By default, no client key file is used."
},
{
"name": "otel.exporter.otlp.logs.compression",
"type": "java.lang.String",
"description": "The compression type to use on OTLP log requests.<br/>Options include <code>gzip</code>.<br/>By default, no compression will be used."
},
{
"name": "otel.exporter.otlp.logs.endpoint",
"type": "java.lang.String",
"description": "The OTLP logs endpoint to connect to.<br/>Must be a URL with a scheme of either <code>http</code> or <code>https</code> based on the use of TLS.<br/>Default is <code>http://localhost:4317</code> when protocol is <code>grpc</code>, and <code>http://localhost:4318/v1/logs</code> when protocol is <code>http/protobuf</code>."
},
{
"name": "otel.exporter.otlp.logs.headers",
"type": "java.util.Map<java.lang.String, java.lang.String>",
"description": "Request headers for OTLP log requests.<br/>Can be either a Spring map or a key-value separated String, e.g. <code>key1=value1,key2=value2</code>."
},
{
"name": "otel.exporter.otlp.logs.protocol",
"type": "java.lang.String",
"description": "The transport protocol to use on OTLP log requests.",
"defaultValue": "http/protobuf"
},
{
"name": "otel.exporter.otlp.logs.timeout",
"type": "java.lang.String",
"description": "The maximum waiting time, in milliseconds, allowed to send each OTLP log batch.<br/>Durations can be of the form <code>{number}{unit}</code>, where unit is one of:<ul><li>ms<li>s<li>m<li>h<li>d</ul><p>If no unit is specified, milliseconds is the assumed duration unit.",
"defaultValue": "10000"
},
{
"name": "otel.exporter.otlp.metrics.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP metric server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default, the host platform's trusted root certificates are used."
},
{
"name": "otel.exporter.otlp.metrics.client.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP metric server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default, no chain file is used."
},
{
"name": "otel.exporter.otlp.metrics.client.key",
"type": "java.lang.String",
"description": "The path to the file containing private client key to use when verifying an OTLP metric client's TLS credentials.<br/>The file should contain one private key PKCS8 PEM format.<br/>By default, no client key file is used."
},
{
"name": "otel.exporter.otlp.metrics.compression",
"type": "java.lang.String",
"description": "The compression type to use on OTLP metric requests.<br/>Options include <code>gzip</code>.<br/>By default, no compression will be used."
},
{
"name": "otel.exporter.otlp.metrics.default.histogram.aggregation",
"type": "java.lang.String",
"description": "The preferred default histogram aggregation.",
"defaultValue": "EXPLICIT_BUCKET_HISTOGRAM"
},
{
"name": "otel.exporter.otlp.metrics.endpoint",
"type": "java.lang.String",
"description": "The OTLP metrics endpoint to connect to.<br/>Must be a URL with a scheme of either <code>http</code> or <code>https</code> based on the use of TLS.<br/>Default is <code>http://localhost:4317</code> when protocol is <code>grpc</code>, and <code>http://localhost:4318/v1/metrics</code> when protocol is <code>http/protobuf</code>."
},
{
"name": "otel.exporter.otlp.metrics.headers",
"type": "java.util.Map<java.lang.String, java.lang.String>",
"description": "Request headers for OTLP metric requests.<br/>Can be either a Spring map or a key-value separated String, e.g. <code>key1=value1,key2=value2</code>."
},
{
"name": "otel.exporter.otlp.metrics.protocol",
"type": "java.lang.String",
"description": "The transport protocol to use on OTLP metric requests.",
"defaultValue": "http/protobuf"
},
{
"name": "otel.exporter.otlp.metrics.temporality.preference",
"type": "java.lang.String",
"description": "The preferred output aggregation temporality.",
"defaultValue": "CUMULATIVE"
},
{
"name": "otel.exporter.otlp.metrics.timeout",
"type": "java.lang.String",
"description": "The maximum waiting time, in milliseconds, allowed to send each OTLP metric batch.<br/>Durations can be of the form <code>{number}{unit}</code>, where unit is one of:<ul><li>ms<li>s<li>m<li>h<li>d</ul><p>If no unit is specified, milliseconds is the assumed duration unit.",
"defaultValue": "10000"
},
{
"name": "otel.exporter.otlp.protocol",
"type": "java.lang.String",
"description": "The transport protocol to use on OTLP trace, metric, and log requests.",
"defaultValue": "http/protobuf"
},
{
"name": "otel.exporter.otlp.timeout",
"type": "java.lang.String",
"description": "The maximum waiting time, in milliseconds, allowed to send each OTLP trace, metric, and log batch.<br/>Durations can be of the form <code>{number}{unit}</code>, where unit is one of:<ul><li>ms<li>s<li>m<li>h<li>d</ul><p>If no unit is specified, milliseconds is the assumed duration unit.",
"defaultValue": "10000"
},
{
"name": "otel.exporter.otlp.traces.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP trace server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default, the host platform's trusted root certificates are used."
},
{
"name": "otel.exporter.otlp.traces.client.certificate",
"type": "java.lang.String",
"description": "The path to the file containing trusted certificates to use when verifying an OTLP trace server's TLS credentials.<br/>The file should contain one or more X.509 certificates in PEM format.<br/>By default no chain file is used."
},
{
"name": "otel.exporter.otlp.traces.client.key",
"type": "java.lang.String",
"description": "The path to the file containing private client key to use when verifying an OTLP trace client's TLS credentials.<br/>The file should contain one private key PKCS8 PEM format.<br/>By default, no client key file is used."
},
{
"name": "otel.exporter.otlp.traces.compression",
"type": "java.lang.String",
"description": "The compression type to use on OTLP trace requests.<br/>Options include <code>gzip</code>.<br/>By default, no compression will be used."
},
{
"name": "otel.exporter.otlp.traces.endpoint",
"type": "java.lang.String",
"description": "The OTLP traces endpoint to connect to.<br/>Must be a URL with a scheme of either <code>http</code> or <code>https</code> based on the use of TLS.<br/>Default is <code>http://localhost:4317</code> when protocol is <code>grpc</code>, and <code>http://localhost:4318/v1/traces</code> when protocol is <code>http/protobuf</code>."
},
{
"name": "otel.exporter.otlp.traces.headers",
"type": "java.util.Map<java.lang.String, java.lang.String>",
"description": "Request headers for OTLP trace requests.<br/>Can be either a Spring map or a key-value separated String, e.g. <code>key1=value1,key2=value2</code>."
},
{
"name": "otel.exporter.otlp.traces.protocol",
"type": "java.lang.String",
"description": "The transport protocol to use on OTLP trace requests.",
"defaultValue": "http/protobuf"
},
{
"name": "otel.exporter.otlp.traces.timeout",
"type": "java.lang.String",
"description": "The maximum waiting time, in milliseconds, allowed to send each OTLP trace batch.<br/>Durations can be of the form <code>{number}{unit}</code>, where unit is one of:<ul><li>ms<li>s<li>m<li>h<li>d</ul><p>If no unit is specified, milliseconds is the assumed duration unit.",
"defaultValue": "10000"
},
{
"name": "otel.exporter.zipkin.endpoint",
"type": "java.lang.String",
"description": "The Zipkin endpoint to connect to.<br/>Currently only HTTP is supported.",
"defaultValue": "http://localhost:9411/api/v2/spans"
},
{
"name": "otel.instrumentation.logback-appender.experimental.capture-code-attributes",
"type": "java.lang.Boolean",
@ -38,7 +230,162 @@
{
"name": "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes",
"type": "java.lang.String",
"description": "Comma separated list of MDC attributes to capture. Use the wildcard character * to capture all attributes."
"description": "Comma separated list of MDC attributes to capture. Use the wildcard character <code>*</code> to capture all attributes."
},
{
"name": "otel.logs.exporter",
"type": "java.lang.String",
"description": "List of exporters to be used for logs, separated by commas.",
"defaultValue": "otlp"
},
{
"name": "otel.metric.export.interval",
"type": "java.lang.String",
"description": "The interval, in milliseconds, between the start of two export attempts.<br/>Durations can be of the form <code>{number}{unit}</code>, where unit is one of:<ul><li>ms<li>s<li>m<li>h<li>d</ul><p>If no unit is specified, milliseconds is the assumed duration unit.",
"defaultValue": "60000"
},
{
"name": "otel.metrics.exporter",
"type": "java.lang.String",
"description": "List of exporters to be used for metrics, separated by commas.",
"defaultValue": "otlp"
},
{
"name": "otel.traces.exporter",
"type": "java.lang.String",
"description": "List of exporters to be used for tracing, separated by commas.",
"defaultValue": "otlp"
},
{
"name": "otel.traces.sampler.probability",
"type": "java.lang.Double",
"description": "The probability of sampling.<br/>The value should be within [0.0, 1.0]. 1.0 means keep everything, 0.0 means drop all spans.",
"defaultValue": 1.0
}
],
"hints": [
{
"name": "otel.exporter.otlp.logs.protocol",
"values": [
{
"value": "http/protobuf"
},
{
"value": "grpc"
}
]
},
{
"name": "otel.exporter.otlp.metrics.protocol",
"values": [
{
"value": "http/protobuf"
},
{
"value": "grpc"
}
]
},
{
"name": "otel.exporter.otlp.protocol",
"values": [
{
"value": "http/protobuf"
},
{
"value": "grpc"
}
]
},
{
"name": "otel.exporter.otlp.traces.protocol",
"values": [
{
"value": "http/protobuf"
},
{
"value": "grpc"
}
]
},
{
"name": "otel.exporter.otlp.metrics.default.histogram.aggregation",
"values": [
{
"value": "BASE2_EXPONENTIAL_BUCKET_HISTOGRAM"
},
{
"value": "EXPLICIT_BUCKET_HISTOGRAM"
}
]
},
{
"name": "otel.exporter.otlp.metrics.temporality.preference",
"values": [
{
"value": "CUMULATIVE",
"description": "All instruments will have cumulative temporality."
},
{
"value": "DELTA",
"description": "Counter (sync and async) and histograms will be delta, up down counters (sync and async) will be cumulative."
},
{
"value": "LOWMEMORY",
"description": "Sync counter and histograms will be delta, async counter and up down counters (sync and async) will be cumulative."
}
]
},
{
"name": "otel.logs.exporter",
"values": [
{
"value": "none",
"description": "No autoconfigured exporter."
},
{
"value": "otlp",
"description": "OpenTelemetry Protocol (OTLP) exporter."
}
]
},
{
"name": "otel.metrics.exporter",
"values": [
{
"value": "logging",
"description": "The logging exporter prints exported metrics to stdout. It's mainly used for testing and debugging."
},
{
"value": "none",
"description": "No autoconfigured exporter."
},
{
"value": "otlp",
"description": "OpenTelemetry Protocol (OTLP) exporter."
}
]
},
{
"name": "otel.traces.exporter",
"values": [
{
"value": "logging",
"description": "The logging exporter prints the name of the span along with its attributes to stdout. It's mainly used for testing and debugging."
},
{
"value": "none",
"description": "No autoconfigured exporter."
},
{
"value": "otlp",
"description": "OpenTelemetry Protocol (OTLP) exporter."
},
{
"value": "zipkin",
"description": "Zipkin exporter."
}
]
}
]
}

View File

@ -2,7 +2,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingMetricExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLoggerExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLogRecordExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration,\

View File

@ -1,7 +1,7 @@
io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingMetricExporterAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLoggerExporterAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLogRecordExporterAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration

View File

@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingMetricExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration;
import org.junit.jupiter.api.Test;
@ -21,6 +22,7 @@ class MetricExporterAutoConfigurationTest {
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class,
OtlpMetricExporterAutoConfiguration.class,
LoggingMetricExporterAutoConfiguration.class));

View File

@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration;
import org.junit.jupiter.api.Test;
@ -21,6 +22,7 @@ class SpanExporterAutoConfigurationTest {
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class,
OtlpSpanExporterAutoConfiguration.class,
LoggingSpanExporterAutoConfiguration.class));

View File

@ -0,0 +1,95 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.core.env.Environment;
import org.springframework.expression.spel.standard.SpelExpressionParser;
class OtlpExporterPropertiesTest {
private static final String[] HEADER_KEYS = {
"otel.exporter.otlp.traces.headers",
"otel.exporter.otlp.metrics.headers",
"otel.exporter.otlp.logs.headers",
"otel.exporter.otlp.headers",
};
private final ApplicationContextRunner contextRunner =
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class, OtelResourceAutoConfiguration.class));
public static Stream<Arguments> headerKeys() {
return Arrays.stream(HEADER_KEYS).map(Arguments::of);
}
@Test
@DisplayName("test all property types")
void allTypes() {
this.contextRunner
.withPropertyValues(
"otel.exporter.otlp.enabled=true",
"otel.exporter.otlp.timeout=1s",
"otel.exporter.otlp.compression=gzip")
.run(
context -> {
ConfigProperties config = getConfig(context);
assertThat(config.getString("otel.exporter.otlp.compression")).isEqualTo("gzip");
assertThat(config.getBoolean("otel.exporter.otlp.enabled")).isTrue();
assertThat(config.getDuration("otel.exporter.otlp.timeout"))
.isEqualByComparingTo(java.time.Duration.ofSeconds(1));
});
}
@ParameterizedTest
@MethodSource("headerKeys")
@DisplayName("should map headers from spring properties")
void mapFlatHeaders(String key) {
this.contextRunner
.withSystemProperties(key + "=a=1,b=2")
.run(
context ->
assertThat(getConfig(context).getMap(key))
.containsExactly(entry("a", "1"), entry("b", "2")));
}
@ParameterizedTest
@MethodSource("headerKeys")
@DisplayName("should map headers from spring application.yaml")
void mapObjectHeaders(String key) {
this.contextRunner
.withPropertyValues(key + ".a=1", key + ".b=2")
.run(
context ->
assertThat(getConfig(context).getMap(key))
.containsExactly(entry("a", "1"), entry("b", "2")));
}
private static ConfigProperties getConfig(AssertableApplicationContext context) {
return new SpringConfigProperties(
context.getBean("environment", Environment.class),
new SpelExpressionParser(),
context.getBean(OtlpExporterProperties.class));
}
}

View File

@ -7,11 +7,8 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
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;
@ -22,7 +19,8 @@ class OtlpLogExporterAutoConfigurationTest {
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class, OtlpLoggerExporterAutoConfiguration.class));
OpenTelemetryAutoConfiguration.class,
OtlpLogRecordExporterAutoConfiguration.class));
@Test
void otlpEnabled() {
@ -64,39 +62,4 @@ class OtlpLogExporterAutoConfigurationTest {
.withPropertyValues("otel.logs.exporter=none")
.run(context -> assertThat(context.containsBean("otelOtlpLogRecordExporter")).isFalse());
}
@Test
void otlpHttpUsedByDefault() {
runner.run(
context ->
assertThat(
context.getBean("otelOtlpLogRecordExporter", OtlpHttpLogRecordExporter.class))
.isNotNull());
}
@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=grpc")
.run(
context ->
assertThat(
context.getBean(
"otelOtlpLogRecordExporter", OtlpGrpcLogRecordExporter.class))
.isNotNull());
}
@Test
@DisplayName("use http when unknown protocol set")
void useHttpWhenAnUnknownProtocolIsSet() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=unknown")
.run(
context ->
assertThat(
context.getBean(
"otelOtlpLogRecordExporter", OtlpHttpLogRecordExporter.class))
.isNotNull());
}
}

View File

@ -8,9 +8,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
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;
@ -33,28 +31,6 @@ class OtlpMetricExporterAutoConfigurationTest {
.isNotNull());
}
@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=grpc")
.run(
context ->
assertThat(context.getBean("otelOtlpMetricExporter", OtlpGrpcMetricExporter.class))
.isNotNull());
}
@Test
@DisplayName("use http when unknown protocol set")
void useHttpWhenAnUnknownProtocolIsSet() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=unknown")
.run(
context ->
assertThat(context.getBean("otelOtlpMetricExporter", OtlpHttpMetricExporter.class))
.isNotNull());
}
@Test
void otlpMetricsEnabled() {
runner
@ -85,12 +61,4 @@ class OtlpMetricExporterAutoConfigurationTest {
.withPropertyValues("otel.metrics.exporter=none")
.run(context -> assertThat(context.containsBean("otelOtlpMetricExporter")).isFalse());
}
@Test
void otlpHttpUsedByDefault() {
runner.run(
context ->
assertThat(context.getBean("otelOtlpMetricExporter", OtlpHttpMetricExporter.class))
.isNotNull());
}
}

View File

@ -7,24 +7,16 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.stream.Collectors;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
/** Spring Boot auto configuration test for {@link OtlpSpanExporterAutoConfiguration}. */
class OtlpSpanExporterAutoConfigurationTest {
private final OtlpHttpSpanExporterBuilder otlpHttpSpanExporterBuilder =
Mockito.mock(OtlpHttpSpanExporterBuilder.class);
private final ApplicationContextRunner contextRunner =
new ApplicationContextRunner()
.withConfiguration(
@ -40,8 +32,6 @@ class OtlpSpanExporterAutoConfigurationTest {
context ->
assertThat(context.getBean("otelOtlpSpanExporter", OtlpHttpSpanExporter.class))
.isNotNull());
Mockito.verifyNoMoreInteractions(otlpHttpSpanExporterBuilder);
}
@Test
@ -68,99 +58,4 @@ class OtlpSpanExporterAutoConfigurationTest {
.withPropertyValues("otel.exporter.otlp.traces.enabled=false")
.run(context -> assertThat(context.containsBean("otelOtlpSpanExporter")).isFalse());
}
@Test
void otlpTracesDisabled() {
this.contextRunner
.withPropertyValues("otel.traces.exporter=none")
.run(context -> assertThat(context.containsBean("otelOtlpSpanExporter")).isFalse());
}
@Test
@DisplayName("when otlp enabled property is MISSING should initialize OtlpHttpSpanExporter bean")
void exporterPresentByDefault() {
this.contextRunner.run(
context ->
assertThat(context.getBean("otelOtlpSpanExporter", OtlpHttpSpanExporter.class))
.isNotNull());
}
@Test
@DisplayName("use http/protobuf when protocol set")
void useHttp() {
this.contextRunner
.withBean(OtlpHttpSpanExporterBuilder.class, () -> otlpHttpSpanExporterBuilder)
.withPropertyValues(
"otel.exporter.otlp.enabled=true",
"otel.exporter.otlp.protocol=http/protobuf",
"otel.exporter.otlp.endpoint=http://localhost:4317",
"otel.exporter.otlp.headers.x=1",
"otel.exporter.otlp.headers.y=2",
"otel.exporter.otlp.timeout=1s")
.run(context -> {});
Mockito.verify(otlpHttpSpanExporterBuilder).build();
Mockito.verify(otlpHttpSpanExporterBuilder).setEndpoint("http://localhost:4317/v1/traces");
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("x", "1");
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("y", "2");
Mockito.verify(otlpHttpSpanExporterBuilder).setTimeout(java.time.Duration.ofSeconds(1));
Mockito.verifyNoMoreInteractions(otlpHttpSpanExporterBuilder);
}
@Test
@DisplayName("use http/protobuf with environment variables for headers using the MapConverter")
void useHttpWithEnv() {
this.contextRunner
.withBean(OtlpHttpSpanExporterBuilder.class, () -> otlpHttpSpanExporterBuilder)
.withPropertyValues(
"otel.exporter.otlp.enabled=true", "otel.exporter.otlp.protocol=http/protobuf")
// are similar to environment variables in that they use the same converters
.withSystemProperties("otel.exporter.otlp.headers=x=1,y=2")
.run(context -> {});
Mockito.verify(otlpHttpSpanExporterBuilder).build();
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("x", "1");
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("y", "2");
Mockito.verifyNoMoreInteractions(otlpHttpSpanExporterBuilder);
}
@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
this.contextRunner
.withPropertyValues("otel.exporter.otlp.protocol=grpc")
.run(
context ->
assertThat(context.getBean(OtlpGrpcSpanExporter.class))
.as("Should contain the gRPC span exporter when grpc is set")
.isNotNull());
}
@Test
@DisplayName("use http when unknown protocol set")
void useHttpWhenAnUnknownProtocolIsSet() {
this.contextRunner
.withPropertyValues("otel.exporter.otlp.protocol=unknown")
.run(
context ->
assertThat(context.getBean(OtlpHttpSpanExporter.class))
.as("Should contain the http span exporter when an unknown is set")
.isNotNull());
}
@Test
@DisplayName("logging exporter can still be configured")
void loggingExporter() {
this.contextRunner
.withBean(
LoggingSpanExporter.class,
LoggingSpanExporter::create,
bd -> bd.setDestroyMethodName(""))
.run(
context ->
assertThat(
context.getBeanProvider(SpanExporter.class).stream()
.collect(Collectors.toList()))
.hasSize(2));
}
}

View File

@ -8,13 +8,14 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.resources;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpExporterProperties;
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 {
class SpringConfigPropertiesTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
@ -26,8 +27,9 @@ class SpringResourceConfigPropertiesTest {
.run(
context -> {
Environment env = context.getBean("environment", Environment.class);
SpringResourceConfigProperties config =
new SpringResourceConfigProperties(env, new SpelExpressionParser());
SpringConfigProperties config =
new SpringConfigProperties(
env, new SpelExpressionParser(), new OtlpExporterProperties());
assertThat(config.getMap("otel.springboot.test.map"))
.contains(