Create spring-boot-opentelemetry module
Renames `management.otlp.logging` properties to `management.opentelemetry.logging`. Issue: 46149
This commit is contained in:
parent
3909b9d483
commit
ded9701db5
|
@ -119,6 +119,7 @@ include "spring-boot-project:spring-boot-metrics"
|
|||
include "spring-boot-project:spring-boot-mongodb"
|
||||
include "spring-boot-project:spring-boot-mustache"
|
||||
include "spring-boot-project:spring-boot-netty"
|
||||
include "spring-boot-project:spring-boot-opentelemetry"
|
||||
include "spring-boot-project:spring-boot-parent"
|
||||
include "spring-boot-project:spring-boot-pulsar"
|
||||
include "spring-boot-project:spring-boot-quartz"
|
||||
|
|
|
@ -56,6 +56,7 @@ dependencies {
|
|||
optional(project(":spring-boot-project:spring-boot-jsonb"))
|
||||
optional(project(":spring-boot-project:spring-boot-kafka"))
|
||||
optional(project(":spring-boot-project:spring-boot-metrics"))
|
||||
optional(project(":spring-boot-project:spring-boot-opentelemetry"))
|
||||
optional(project(":spring-boot-project:spring-boot-r2dbc"))
|
||||
optional(project(":spring-boot-project:spring-boot-restclient"))
|
||||
optional(project(":spring-boot-project:spring-boot-security-oauth2-client"))
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.actuate.autoconfigure.logging;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.sdk.logs.LogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry logging.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
* @since 3.4.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ SdkLoggerProvider.class, OpenTelemetry.class })
|
||||
public class OpenTelemetryLoggingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BatchLogRecordProcessor batchLogRecordProcessor(ObjectProvider<LogRecordExporter> logRecordExporters) {
|
||||
return BatchLogRecordProcessor.builder(LogRecordExporter.composite(logRecordExporters.orderedStream().toList()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SdkLoggerProvider otelSdkLoggerProvider(Resource resource, ObjectProvider<LogRecordProcessor> logRecordProcessors,
|
||||
ObjectProvider<SdkLoggerProviderBuilderCustomizer> customizers) {
|
||||
SdkLoggerProviderBuilder builder = SdkLoggerProvider.builder().setResource(resource);
|
||||
logRecordProcessors.orderedStream().forEach(builder::addLogRecordProcessor);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.actuate.autoconfigure.logging.otlp;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import io.opentelemetry.api.metrics.MeterProvider;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
|
||||
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
|
||||
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.ConditionalOnEnabledLoggingExport;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configurations imported by {@link OtlpLoggingAutoConfiguration}.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
final class OtlpLoggingConfigurations {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetails {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty("management.otlp.logging.endpoint")
|
||||
OtlpLoggingConnectionDetails otlpLoggingConnectionDetails(OtlpLoggingProperties properties) {
|
||||
return new PropertiesOtlpLoggingConnectionDetails(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link OtlpLoggingProperties} to {@link OtlpLoggingConnectionDetails}.
|
||||
*/
|
||||
static class PropertiesOtlpLoggingConnectionDetails implements OtlpLoggingConnectionDetails {
|
||||
|
||||
private final OtlpLoggingProperties properties;
|
||||
|
||||
PropertiesOtlpLoggingConnectionDetails(OtlpLoggingProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(Transport transport) {
|
||||
Assert.state(transport == this.properties.getTransport(),
|
||||
"Requested transport %s doesn't match configured transport %s".formatted(transport,
|
||||
this.properties.getTransport()));
|
||||
return this.properties.getEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean({ OtlpGrpcLogRecordExporter.class, OtlpHttpLogRecordExporter.class })
|
||||
@ConditionalOnBean(OtlpLoggingConnectionDetails.class)
|
||||
@ConditionalOnEnabledLoggingExport("otlp")
|
||||
static class Exporters {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "management.otlp.logging.transport", havingValue = "http", matchIfMissing = true)
|
||||
OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties properties,
|
||||
OtlpLoggingConnectionDetails connectionDetails, ObjectProvider<MeterProvider> meterProvider) {
|
||||
OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder()
|
||||
.setEndpoint(connectionDetails.getUrl(Transport.HTTP))
|
||||
.setTimeout(properties.getTimeout())
|
||||
.setConnectTimeout(properties.getConnectTimeout())
|
||||
.setCompression(properties.getCompression().name().toLowerCase(Locale.US));
|
||||
properties.getHeaders().forEach(builder::addHeader);
|
||||
meterProvider.ifAvailable(builder::setMeterProvider);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "management.otlp.logging.transport", havingValue = "grpc")
|
||||
OtlpGrpcLogRecordExporter otlpGrpcLogRecordExporter(OtlpLoggingProperties properties,
|
||||
OtlpLoggingConnectionDetails connectionDetails, ObjectProvider<MeterProvider> meterProvider) {
|
||||
OtlpGrpcLogRecordExporterBuilder builder = OtlpGrpcLogRecordExporter.builder()
|
||||
.setEndpoint(connectionDetails.getUrl(Transport.GRPC))
|
||||
.setTimeout(properties.getTimeout())
|
||||
.setConnectTimeout(properties.getConnectTimeout())
|
||||
.setCompression(properties.getCompression().name().toLowerCase(Locale.US));
|
||||
properties.getHeaders().forEach(builder::addHeader);
|
||||
meterProvider.ifAvailable(builder::setMeterProvider);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@ import io.micrometer.registry.otlp.OtlpMetricsSender;
|
|||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
|
@ -35,6 +34,7 @@ import org.springframework.boot.autoconfigure.thread.Threading;
|
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration;
|
||||
import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
||||
|
|
|
@ -28,8 +28,8 @@ import io.micrometer.registry.otlp.OtlpConfig;
|
|||
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsProperties.Meter;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryResourceAttributes;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryProperties;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryResourceAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
|
|
|
@ -11,10 +11,6 @@ org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfi
|
|||
org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.logging.OpenTelemetryLoggingAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.management.HeapDumpWebEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.metrics.data.RepositoryMetricsAutoConfiguration
|
||||
|
@ -44,7 +40,6 @@ org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfig
|
|||
org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.r2dbc.ConnectionFactoryHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.r2dbc.R2dbcObservationAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.sbom.SbomEndpointAutoConfiguration
|
||||
|
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.actuate.autoconfigure.logging;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||
import io.opentelemetry.sdk.logs.LogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||
import io.opentelemetry.sdk.logs.data.LogRecordData;
|
||||
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OpenTelemetryLoggingAutoConfiguration}.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
class OpenTelemetryLoggingAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner;
|
||||
|
||||
OpenTelemetryLoggingAutoConfigurationTests() {
|
||||
this.contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations
|
||||
.of(OpenTelemetryAutoConfiguration.class, OpenTelemetryLoggingAutoConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupplyBeans() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "io.opentelemetry.sdk.logs", "io.opentelemetry.api" })
|
||||
void shouldNotSupplyBeansIfDependencyIsMissing(String packageName) {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(packageName)).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).doesNotHaveBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffOnCustomBeans() {
|
||||
this.contextRunner.withUserConfiguration(CustomConfig.class).run((context) -> {
|
||||
assertThat(context).hasBean("customBatchLogRecordProcessor").hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context.getBeansOfType(LogRecordProcessor.class)).hasSize(1);
|
||||
assertThat(context).hasBean("customSdkLoggerProvider").hasSingleBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowMultipleLogRecordExporters() {
|
||||
this.contextRunner.withUserConfiguration(MultipleLogRecordExportersConfig.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context.getBeansOfType(LogRecordExporter.class)).hasSize(2);
|
||||
assertThat(context).hasBean("customLogRecordExporter1");
|
||||
assertThat(context).hasBean("customLogRecordExporter2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowMultipleLogRecordProcessorsInAdditionToBatchLogRecordProcessor() {
|
||||
this.contextRunner.withUserConfiguration(MultipleLogRecordProcessorsConfig.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class);
|
||||
assertThat(context.getBeansOfType(LogRecordProcessor.class)).hasSize(3);
|
||||
assertThat(context).hasBean("batchLogRecordProcessor");
|
||||
assertThat(context).hasBean("customLogRecordProcessor1");
|
||||
assertThat(context).hasBean("customLogRecordProcessor2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowMultipleSdkLoggerProviderBuilderCustomizers() {
|
||||
this.contextRunner.withUserConfiguration(MultipleSdkLoggerProviderBuilderCustomizersConfig.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class);
|
||||
assertThat(context.getBeansOfType(SdkLoggerProviderBuilderCustomizer.class)).hasSize(2);
|
||||
assertThat(context).hasBean("customSdkLoggerProviderBuilderCustomizer1");
|
||||
assertThat(context).hasBean("customSdkLoggerProviderBuilderCustomizer2");
|
||||
assertThat(context
|
||||
.getBean("customSdkLoggerProviderBuilderCustomizer1", NoopSdkLoggerProviderBuilderCustomizer.class)
|
||||
.called()).isEqualTo(1);
|
||||
assertThat(context
|
||||
.getBean("customSdkLoggerProviderBuilderCustomizer2", NoopSdkLoggerProviderBuilderCustomizer.class)
|
||||
.called()).isEqualTo(1);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class CustomConfig {
|
||||
|
||||
@Bean
|
||||
public BatchLogRecordProcessor customBatchLogRecordProcessor() {
|
||||
return BatchLogRecordProcessor.builder(new NoopLogRecordExporter()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SdkLoggerProvider customSdkLoggerProvider() {
|
||||
return SdkLoggerProvider.builder().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class MultipleLogRecordExportersConfig {
|
||||
|
||||
@Bean
|
||||
public LogRecordExporter customLogRecordExporter1() {
|
||||
return new NoopLogRecordExporter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LogRecordExporter customLogRecordExporter2() {
|
||||
return new NoopLogRecordExporter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class MultipleLogRecordProcessorsConfig {
|
||||
|
||||
@Bean
|
||||
public LogRecordProcessor customLogRecordProcessor1() {
|
||||
return new NoopLogRecordProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LogRecordProcessor customLogRecordProcessor2() {
|
||||
return new NoopLogRecordProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class MultipleSdkLoggerProviderBuilderCustomizersConfig {
|
||||
|
||||
@Bean
|
||||
public SdkLoggerProviderBuilderCustomizer customSdkLoggerProviderBuilderCustomizer1() {
|
||||
return new NoopSdkLoggerProviderBuilderCustomizer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SdkLoggerProviderBuilderCustomizer customSdkLoggerProviderBuilderCustomizer2() {
|
||||
return new NoopSdkLoggerProviderBuilderCustomizer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopLogRecordExporter implements LogRecordExporter {
|
||||
|
||||
@Override
|
||||
public CompletableResultCode export(Collection<LogRecordData> logs) {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode flush() {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode shutdown() {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopLogRecordProcessor implements LogRecordProcessor {
|
||||
|
||||
@Override
|
||||
public void onEmit(Context context, ReadWriteLogRecord logRecord) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopSdkLoggerProviderBuilderCustomizer implements SdkLoggerProviderBuilderCustomizer {
|
||||
|
||||
final AtomicInteger called = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void customize(SdkLoggerProviderBuilder builder) {
|
||||
this.called.incrementAndGet();
|
||||
}
|
||||
|
||||
int called() {
|
||||
return this.called.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsExportAutoConfiguration.PropertiesOtlpMetricsConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsProperties.Meter;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryProperties;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.actuate.autoconfigure.opentelemetry;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link OpenTelemetryAutoConfiguration}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class OpenTelemetryAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void isRegisteredInAutoConfigurationImports() {
|
||||
assertThat(ImportCandidates.load(AutoConfiguration.class, null).getCandidates())
|
||||
.contains(OpenTelemetryAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProvideBeans() {
|
||||
this.runner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffIfOpenTelemetryIsNotOnClasspath() {
|
||||
this.runner.withClassLoader(new FilteredClassLoader("io.opentelemetry")).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).doesNotHaveBean(Resource.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffOnUserSuppliedBeans() {
|
||||
this.runner.withUserConfiguration(UserConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetry.class);
|
||||
assertThat(context).hasBean("customOpenTelemetry");
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
assertThat(context).hasBean("customResource");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplySpringApplicationNameToResource() {
|
||||
this.runner.withPropertyValues("spring.application.name=my-application").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("service.name"), "my-application"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplySpringApplicationGroupToResource() {
|
||||
this.runner.withPropertyValues("spring.application.group=my-group").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("service.group"), "my-group"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotApplySpringApplicationGroupIfNotSet() {
|
||||
this.runner.run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("service.group"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplyServiceNamespaceIfApplicationGroupIsSet() {
|
||||
this.runner.withPropertyValues("spring.application.group=my-group").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).containsEntry(AttributeKey.stringKey("service.namespace"),
|
||||
"my-group");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotApplyServiceNamespaceIfApplicationGroupIsNotSet() {
|
||||
this.runner.run(((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("service.namespace"));
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallbackToDefaultApplicationNameIfSpringApplicationNameIsNotSet() {
|
||||
this.runner.run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("service.name"), "unknown_service"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplyResourceAttributesFromProperties() {
|
||||
this.runner.withPropertyValues("management.opentelemetry.resource-attributes.region=us-west").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).contains(entry(AttributeKey.stringKey("region"), "us-west"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterSdkTracerProviderIfAvailable() {
|
||||
this.runner.withBean(SdkTracerProvider.class, () -> SdkTracerProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getTracerProvider()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterContextPropagatorsIfAvailable() {
|
||||
this.runner.withBean(ContextPropagators.class, ContextPropagators::noop).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getPropagators()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterSdkLoggerProviderIfAvailable() {
|
||||
this.runner.withBean(SdkLoggerProvider.class, () -> SdkLoggerProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getLogsBridge()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterSdkMeterProviderIfAvailable() {
|
||||
this.runner.withBean(SdkMeterProvider.class, () -> SdkMeterProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getMeterProvider()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UserConfiguration {
|
||||
|
||||
@Bean
|
||||
OpenTelemetry customOpenTelemetry() {
|
||||
return mock(OpenTelemetry.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Resource customResource() {
|
||||
return Resource.getDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -30,8 +30,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.classpath.ForkedClassPath;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -173,7 +173,7 @@ class BaggagePropagationIntegrationTests {
|
|||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer())
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
"management.tracing.baggage.correlation.fields=country-code,bp");
|
||||
|
@ -199,7 +199,7 @@ class BaggagePropagationIntegrationTests {
|
|||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer())
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.propagation.type=W3C",
|
||||
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
|
@ -239,7 +239,7 @@ class BaggagePropagationIntegrationTests {
|
|||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer())
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.propagation.type=B3",
|
||||
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
|
@ -253,7 +253,7 @@ class BaggagePropagationIntegrationTests {
|
|||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer())
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.propagation.type=B3_MULTI",
|
||||
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
|
|
|
@ -90,7 +90,7 @@ class OpenTelemetryTracingAutoConfigurationTests {
|
|||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration.class,
|
||||
org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration.class,
|
||||
OpenTelemetryTracingAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
|
|
|
@ -47,10 +47,10 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingAutoConfigurationIntegrationTests.MockGrpcServer.RecordedGrpcRequest;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -65,7 +65,7 @@ class OtlpTracingAutoConfigurationIntegrationTests {
|
|||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("management.tracing.sampling.probability=1.0")
|
||||
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class,
|
||||
MicrometerTracingAutoConfiguration.class, OpenTelemetryAutoConfiguration.class,
|
||||
MicrometerTracingAutoConfiguration.class, OpenTelemetrySdkAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class,
|
||||
OtlpTracingAutoConfiguration.class));
|
||||
|
||||
|
|
|
@ -4,4 +4,6 @@ org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfi
|
|||
org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration
|
||||
|
|
|
@ -2048,6 +2048,7 @@ bom {
|
|||
"spring-boot-mustache",
|
||||
"spring-boot-neo4j",
|
||||
"spring-boot-netty",
|
||||
"spring-boot-opentelemetry",
|
||||
"spring-boot-properties-migrator",
|
||||
"spring-boot-pulsar",
|
||||
"spring-boot-quartz",
|
||||
|
|
|
@ -32,6 +32,7 @@ dependencies {
|
|||
dockerTestImplementation(project(":spring-boot-project:spring-boot-jdbc"))
|
||||
dockerTestImplementation(project(":spring-boot-project:spring-boot-flyway"))
|
||||
dockerTestImplementation(project(":spring-boot-project:spring-boot-liquibase"))
|
||||
dockerTestImplementation(project(":spring-boot-project:spring-boot-opentelemetry"))
|
||||
dockerTestImplementation(project(":spring-boot-project:spring-boot-pulsar"))
|
||||
dockerTestImplementation(project(":spring-boot-project:spring-boot-r2dbc"))
|
||||
dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker"))
|
||||
|
@ -45,6 +46,7 @@ dependencies {
|
|||
dockerTestRuntimeOnly("com.clickhouse:clickhouse-r2dbc")
|
||||
dockerTestRuntimeOnly("com.microsoft.sqlserver:mssql-jdbc")
|
||||
dockerTestRuntimeOnly("com.oracle.database.r2dbc:oracle-r2dbc")
|
||||
dockerTestRuntimeOnly("io.opentelemetry:opentelemetry-exporter-otlp")
|
||||
dockerTestRuntimeOnly("io.r2dbc:r2dbc-mssql")
|
||||
dockerTestRuntimeOnly("org.postgresql:postgresql")
|
||||
dockerTestRuntimeOnly("org.postgresql:r2dbc-postgresql")
|
||||
|
@ -67,6 +69,7 @@ dependencies {
|
|||
optional(project(":spring-boot-project:spring-boot-liquibase"))
|
||||
optional(project(":spring-boot-project:spring-boot-mongodb"))
|
||||
optional(project(":spring-boot-project:spring-boot-neo4j"))
|
||||
optional(project(":spring-boot-project:spring-boot-opentelemetry"))
|
||||
optional(project(":spring-boot-project:spring-boot-pulsar"))
|
||||
optional(project(":spring-boot-project:spring-boot-r2dbc"))
|
||||
optional(project(":spring-boot-project:spring-boot-zipkin"))
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.service.connection.otlp;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
class GrafanaOpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.GRAFANA_OTEL_LGTM)
|
||||
void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) {
|
||||
void runCreatesConnectionDetails(OpenTelemetryLoggingConnectionDetails connectionDetails) {
|
||||
assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/logs");
|
||||
assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/logs");
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.service.connection.otlp;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
class OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.OPENTELEMETRY)
|
||||
void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) {
|
||||
void runCreatesConnectionDetails(OpenTelemetryLoggingConnectionDetails connectionDetails) {
|
||||
assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/logs");
|
||||
assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/logs");
|
||||
}
|
||||
|
|
|
@ -16,20 +16,20 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.service.connection.otlp;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.docker.compose.core.RunningService;
|
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
|
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
|
||||
/**
|
||||
* {@link DockerComposeConnectionDetailsFactory} to create
|
||||
* {@link OtlpLoggingConnectionDetails} for an OTLP service.
|
||||
* {@link OpenTelemetryLoggingConnectionDetails} for an OTLP service.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class OpenTelemetryLoggingDockerComposeConnectionDetailsFactory
|
||||
extends DockerComposeConnectionDetailsFactory<OtlpLoggingConnectionDetails> {
|
||||
extends DockerComposeConnectionDetailsFactory<OpenTelemetryLoggingConnectionDetails> {
|
||||
|
||||
private static final String[] OPENTELEMETRY_IMAGE_NAMES = { "otel/opentelemetry-collector-contrib",
|
||||
"grafana/otel-lgtm" };
|
||||
|
@ -40,16 +40,17 @@ class OpenTelemetryLoggingDockerComposeConnectionDetailsFactory
|
|||
|
||||
OpenTelemetryLoggingDockerComposeConnectionDetailsFactory() {
|
||||
super(OPENTELEMETRY_IMAGE_NAMES,
|
||||
"org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration");
|
||||
"org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportAutoConfiguration");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OtlpLoggingConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) {
|
||||
protected OpenTelemetryLoggingConnectionDetails getDockerComposeConnectionDetails(
|
||||
DockerComposeConnectionSource source) {
|
||||
return new OpenTelemetryLoggingDockerComposeConnectionDetails(source.getRunningService());
|
||||
}
|
||||
|
||||
private static final class OpenTelemetryLoggingDockerComposeConnectionDetails extends DockerComposeConnectionDetails
|
||||
implements OtlpLoggingConnectionDetails {
|
||||
implements OpenTelemetryLoggingConnectionDetails {
|
||||
|
||||
private final String host;
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ dependencies {
|
|||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-mustache", configuration: "autoConfigurationMetadata"))
|
||||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-neo4j", configuration: "autoConfigurationMetadata"))
|
||||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-netty", configuration: "autoConfigurationMetadata"))
|
||||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-opentelemetry", configuration: "autoConfigurationMetadata"))
|
||||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-pulsar", configuration: "autoConfigurationMetadata"))
|
||||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-quartz", configuration: "autoConfigurationMetadata"))
|
||||
autoConfiguration(project(path: ":spring-boot-project:spring-boot-r2dbc", configuration: "autoConfigurationMetadata"))
|
||||
|
@ -212,6 +213,7 @@ dependencies {
|
|||
configurationProperties(project(path: ":spring-boot-project:spring-boot-mustache", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-boot-project:spring-boot-neo4j", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-boot-project:spring-boot-netty", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-boot-project:spring-boot-opentelemetry", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-boot-project:spring-boot-pulsar", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-boot-project:spring-boot-quartz", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-boot-project:spring-boot-r2dbc", configuration: "configurationPropertiesMetadata"))
|
||||
|
|
|
@ -42,8 +42,9 @@ You have to provide the location of the OpenTelemetry logs endpoint to configure
|
|||
[configprops,yaml]
|
||||
----
|
||||
management:
|
||||
otlp:
|
||||
opentelemetry:
|
||||
logging:
|
||||
export:
|
||||
endpoint: "https://otlp.example.com:4318/v1/logs"
|
||||
----
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ The following service connections are currently supported:
|
|||
| javadoc:org.springframework.boot.neo4j.autoconfigure.Neo4jConnectionDetails[]
|
||||
| Containers named "neo4j" or "bitnami/neo4j"
|
||||
|
||||
| javadoc:org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails[]
|
||||
| javadoc:org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails[]
|
||||
| Containers named "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm"
|
||||
|
||||
| javadoc:org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsConnectionDetails[]
|
||||
|
|
|
@ -155,7 +155,7 @@ The following service connection factories are provided in the `spring-boot-test
|
|||
| javadoc:org.springframework.boot.neo4j.autoconfigure.Neo4jConnectionDetails[]
|
||||
| Containers of type javadoc:{url-testcontainers-neo4j-javadoc}/org.testcontainers.containers.Neo4jContainer[]
|
||||
|
||||
| javadoc:org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails[]
|
||||
| javadoc:org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails[]
|
||||
| Containers named "otel/opentelemetry-collector-contrib" or of type javadoc:org.testcontainers.grafana.LgtmStackContainer[]
|
||||
|
||||
| javadoc:org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsConnectionDetails[]
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
|
||||
plugins {
|
||||
id "java-library"
|
||||
id "org.springframework.boot.auto-configuration"
|
||||
id "org.springframework.boot.configuration-properties"
|
||||
id "org.springframework.boot.deployed"
|
||||
id "org.springframework.boot.docker-test"
|
||||
id "org.springframework.boot.optional-dependencies"
|
||||
}
|
||||
|
||||
description = "Spring Boot Open Telemetry"
|
||||
|
||||
dependencies {
|
||||
api(project(":spring-boot-project:spring-boot"))
|
||||
api("io.opentelemetry:opentelemetry-api")
|
||||
api("io.opentelemetry:opentelemetry-sdk")
|
||||
|
||||
optional(project(":spring-boot-project:spring-boot-autoconfigure"))
|
||||
optional(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
|
||||
optional("io.opentelemetry:opentelemetry-exporter-otlp")
|
||||
|
||||
testImplementation(project(":spring-boot-project:spring-boot-test"))
|
||||
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
|
||||
testImplementation("com.squareup.okhttp3:mockwebserver")
|
||||
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
testRuntimeOnly("io.grpc:grpc-api:1.72.0")
|
||||
}
|
|
@ -14,17 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an OpenTelemetry logging service.
|
||||
* Details required for actuator to establish a connection to an OpenTelemetry logging
|
||||
* service.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface OtlpLoggingConnectionDetails extends ConnectionDetails {
|
||||
public interface OpenTelemetryLoggingConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Address to where logs will be published.
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} for {@link OpenTelemetryLoggingConnectionDetails}.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class OpenTelemetryLoggingConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty("management.opentelemetry.logging.export.endpoint")
|
||||
PropertiesOpenTelemetryLoggingConnectionDetails openTelemetryLoggingConnectionDetails(
|
||||
OpenTelemetryLoggingExportProperties properties) {
|
||||
return new PropertiesOpenTelemetryLoggingConnectionDetails(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link OpenTelemetryLoggingExportProperties} to
|
||||
* {@link OpenTelemetryLoggingConnectionDetails}.
|
||||
*/
|
||||
static class PropertiesOpenTelemetryLoggingConnectionDetails implements OpenTelemetryLoggingConnectionDetails {
|
||||
|
||||
private final OpenTelemetryLoggingExportProperties properties;
|
||||
|
||||
PropertiesOpenTelemetryLoggingConnectionDetails(OpenTelemetryLoggingExportProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(Transport transport) {
|
||||
Assert.state(transport == this.properties.getTransport(),
|
||||
"Requested transport %s doesn't match configured transport %s".formatted(transport,
|
||||
this.properties.getTransport()));
|
||||
return this.properties.getEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,12 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.ConditionalOnEnabledLoggingExport;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
|
@ -27,15 +27,16 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OTLP logging.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry logging exports.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ SdkLoggerProvider.class, OpenTelemetry.class, OtlpHttpLogRecordExporter.class })
|
||||
@EnableConfigurationProperties(OtlpLoggingProperties.class)
|
||||
@Import({ OtlpLoggingConfigurations.ConnectionDetails.class, OtlpLoggingConfigurations.Exporters.class })
|
||||
public class OtlpLoggingAutoConfiguration {
|
||||
@ConditionalOnClass({ ConditionalOnEnabledLoggingExport.class, OpenTelemetry.class, SdkLoggerProvider.class })
|
||||
@ConditionalOnEnabledLoggingExport("opentelemetry")
|
||||
@EnableConfigurationProperties(OpenTelemetryLoggingExportProperties.class)
|
||||
@Import({ OpenTelemetryLoggingConnectionDetailsConfiguration.class, OpenTelemetryLoggingTransportConfiguration.class })
|
||||
public class OpenTelemetryLoggingExportAutoConfiguration {
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
|
@ -23,13 +23,13 @@ import java.util.Map;
|
|||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for exporting logs using OTLP.
|
||||
* Configuration properties for exporting logs using OpenTelemetry.
|
||||
*
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("management.otlp.logging")
|
||||
public class OtlpLoggingProperties {
|
||||
@ConfigurationProperties("management.opentelemetry.logging.export")
|
||||
public class OpenTelemetryLoggingExportProperties {
|
||||
|
||||
/**
|
||||
* URL to the OTel collector's HTTP API.
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import io.opentelemetry.api.metrics.MeterProvider;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
|
||||
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
|
||||
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} for OpenTelemetry log record exporters.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(OtlpHttpLogRecordExporter.class)
|
||||
@ConditionalOnMissingBean({ OtlpGrpcLogRecordExporter.class, OtlpHttpLogRecordExporter.class })
|
||||
@ConditionalOnBean(OpenTelemetryLoggingConnectionDetails.class)
|
||||
class OpenTelemetryLoggingTransportConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "management.opentelemetry.logging.export.transport", havingValue = "http",
|
||||
matchIfMissing = true)
|
||||
OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OpenTelemetryLoggingExportProperties properties,
|
||||
OpenTelemetryLoggingConnectionDetails connectionDetails, ObjectProvider<MeterProvider> meterProvider) {
|
||||
OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder()
|
||||
.setEndpoint(connectionDetails.getUrl(Transport.HTTP))
|
||||
.setTimeout(properties.getTimeout())
|
||||
.setConnectTimeout(properties.getConnectTimeout())
|
||||
.setCompression(properties.getCompression().name().toLowerCase(Locale.US));
|
||||
properties.getHeaders().forEach(builder::addHeader);
|
||||
meterProvider.ifAvailable(builder::setMeterProvider);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "management.opentelemetry.logging.export.transport", havingValue = "grpc")
|
||||
OtlpGrpcLogRecordExporter otlpGrpcLogRecordExporter(OpenTelemetryLoggingExportProperties properties,
|
||||
OpenTelemetryLoggingConnectionDetails connectionDetails, ObjectProvider<MeterProvider> meterProvider) {
|
||||
OtlpGrpcLogRecordExporterBuilder builder = OtlpGrpcLogRecordExporter.builder()
|
||||
.setEndpoint(connectionDetails.getUrl(Transport.GRPC))
|
||||
.setTimeout(properties.getTimeout())
|
||||
.setConnectTimeout(properties.getConnectTimeout())
|
||||
.setCompression(properties.getCompression().name().toLowerCase(Locale.US));
|
||||
properties.getHeaders().forEach(builder::addHeader);
|
||||
meterProvider.ifAvailable(builder::setMeterProvider);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,13 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
/**
|
||||
* Transport used to send OTLP data.
|
||||
* Transport used to send OTLP log data.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public enum Transport {
|
||||
|
|
@ -15,6 +15,6 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for exporting logs with OTLP.
|
||||
* Auto-configuration for exporting logs with OpenTelemetry.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -39,9 +39,9 @@ import org.springframework.util.StringUtils;
|
|||
* Resource Specification</a>
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
* @since 3.5.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public final class OpenTelemetryResourceAttributes {
|
||||
public class OpenTelemetryResourceAttributes {
|
||||
|
||||
/**
|
||||
* Default value for service name if {@code service.name} is not set.
|
||||
|
@ -52,7 +52,7 @@ public final class OpenTelemetryResourceAttributes {
|
|||
|
||||
private final Map<String, String> resourceAttributes;
|
||||
|
||||
private final Function<String, String> getEnv;
|
||||
private final Function<String, String> systemEnvironment;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link OpenTelemetryResourceAttributes}.
|
||||
|
@ -67,14 +67,14 @@ public final class OpenTelemetryResourceAttributes {
|
|||
* Creates a new {@link OpenTelemetryResourceAttributes} instance.
|
||||
* @param environment the environment
|
||||
* @param resourceAttributes user-provided resource attributes to be used
|
||||
* @param getEnv a function to retrieve environment variables by name
|
||||
* @param systemEnvironment a function to retrieve environment variables by name
|
||||
*/
|
||||
OpenTelemetryResourceAttributes(Environment environment, Map<String, String> resourceAttributes,
|
||||
Function<String, String> getEnv) {
|
||||
Function<String, String> systemEnvironment) {
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
this.environment = environment;
|
||||
this.resourceAttributes = (resourceAttributes != null) ? resourceAttributes : Collections.emptyMap();
|
||||
this.getEnv = (getEnv != null) ? getEnv : System::getenv;
|
||||
this.systemEnvironment = (systemEnvironment != null) ? systemEnvironment : System::getenv;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,7 +150,7 @@ public final class OpenTelemetryResourceAttributes {
|
|||
}
|
||||
|
||||
private String getEnv(String name) {
|
||||
return this.getEnv.apply(name);
|
||||
return this.systemEnvironment.apply(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,17 +166,17 @@ public final class OpenTelemetryResourceAttributes {
|
|||
return value;
|
||||
}
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
byte b = bytes[i];
|
||||
if (b != '%') {
|
||||
bos.write(b);
|
||||
out.write(b);
|
||||
continue;
|
||||
}
|
||||
int u = decodeHex(bytes, i + 1);
|
||||
int l = decodeHex(bytes, i + 2);
|
||||
if (u >= 0 && l >= 0) {
|
||||
bos.write((u << 4) + l);
|
||||
out.write((u << 4) + l);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -185,7 +185,7 @@ public final class OpenTelemetryResourceAttributes {
|
|||
}
|
||||
i += 2;
|
||||
}
|
||||
return bos.toString(StandardCharsets.UTF_8);
|
||||
return out.toString(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static int decodeHex(byte[] bytes, int index) {
|
|
@ -14,13 +14,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdkBuilder;
|
||||
import io.opentelemetry.sdk.logs.LogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.resources.ResourceBuilder;
|
||||
|
@ -33,37 +37,41 @@ 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.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for the OpenTelemetry SDK.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.2.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(OpenTelemetrySdk.class)
|
||||
@ConditionalOnClass({ OpenTelemetry.class, OpenTelemetrySdk.class })
|
||||
@EnableConfigurationProperties(OpenTelemetryProperties.class)
|
||||
public class OpenTelemetryAutoConfiguration {
|
||||
public class OpenTelemetrySdkAutoConfiguration {
|
||||
|
||||
OpenTelemetrySdkAutoConfiguration() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OpenTelemetry.class)
|
||||
OpenTelemetrySdk openTelemetry(ObjectProvider<SdkTracerProvider> tracerProvider,
|
||||
ObjectProvider<ContextPropagators> propagators, ObjectProvider<SdkLoggerProvider> loggerProvider,
|
||||
ObjectProvider<SdkMeterProvider> meterProvider) {
|
||||
OpenTelemetrySdk openTelemetrySdk(ObjectProvider<SdkTracerProvider> openTelemetrySdkTracerProvider,
|
||||
ObjectProvider<ContextPropagators> openTelemetryContextPropagators,
|
||||
ObjectProvider<SdkLoggerProvider> openTelemetrySdkLoggerProvider,
|
||||
ObjectProvider<SdkMeterProvider> openTelemetrySdkMeterProvider) {
|
||||
OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder();
|
||||
tracerProvider.ifAvailable(builder::setTracerProvider);
|
||||
propagators.ifAvailable(builder::setPropagators);
|
||||
loggerProvider.ifAvailable(builder::setLoggerProvider);
|
||||
meterProvider.ifAvailable(builder::setMeterProvider);
|
||||
openTelemetrySdkTracerProvider.ifAvailable(builder::setTracerProvider);
|
||||
openTelemetryContextPropagators.ifAvailable(builder::setPropagators);
|
||||
openTelemetrySdkLoggerProvider.ifAvailable(builder::setLoggerProvider);
|
||||
openTelemetrySdkMeterProvider.ifAvailable(builder::setMeterProvider);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
Resource openTelemetryResource(Environment environment, OpenTelemetryProperties properties) {
|
||||
Resource resource = Resource.getDefault();
|
||||
return resource.merge(toResource(environment, properties));
|
||||
return Resource.getDefault().merge(toResource(environment, properties));
|
||||
}
|
||||
|
||||
private Resource toResource(Environment environment, OpenTelemetryProperties properties) {
|
||||
|
@ -72,4 +80,30 @@ public class OpenTelemetryAutoConfiguration {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SdkLoggerProvider.class)
|
||||
static class LoggerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BatchLogRecordProcessor openTelemetryBatchLogRecordProcessor(
|
||||
ObjectProvider<LogRecordExporter> logRecordExporters) {
|
||||
LogRecordExporter exporter = LogRecordExporter.composite(logRecordExporters.orderedStream().toList());
|
||||
return BatchLogRecordProcessor.builder(exporter).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SdkLoggerProvider openTelemetrySdkLoggerProvider(Resource openTelemetryResource,
|
||||
ObjectProvider<LogRecordProcessor> logRecordProcessors,
|
||||
ObjectProvider<SdkLoggerProviderBuilderCustomizer> customizers) {
|
||||
SdkLoggerProviderBuilder builder = SdkLoggerProvider.builder();
|
||||
builder.setResource(openTelemetryResource);
|
||||
logRecordProcessors.orderedStream().forEach(builder::addLogRecordProcessor);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||
|
@ -24,7 +24,7 @@ import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
|||
* that is used to create the auto-configured {@link SdkLoggerProvider}.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SdkLoggerProviderBuilderCustomizer {
|
|
@ -17,4 +17,4 @@
|
|||
/**
|
||||
* Auto-configuration for OpenTelemetry.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
"groups": [],
|
||||
"properties": [
|
||||
{
|
||||
"name": "management.logging.export.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether auto-configuration of logging is enabled to export logs.",
|
||||
"defaultValue": true,
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.enabled",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging",
|
||||
"type": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.compression",
|
||||
"type": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties$Compression",
|
||||
"description": "Method used to compress the payload.",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"defaultValue": "none",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.compression",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.connect-timeout",
|
||||
"type": "java.time.Duration",
|
||||
"description": "Connect timeout for the OTel collector connection.",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"defaultValue": "10s",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.connect-timeout",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.endpoint",
|
||||
"type": "java.lang.String",
|
||||
"description": "URL to the OTel collector's HTTP API.",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.endpoint",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.export.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether auto-configuration of logging is enabled to export OTLP logs.",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.enabled",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.headers",
|
||||
"type": "java.util.Map<java.lang.String,java.lang.String>",
|
||||
"description": "Custom HTTP headers you want to pass to the collector, for example auth headers.",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.headers",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.timeout",
|
||||
"type": "java.time.Duration",
|
||||
"description": "Call timeout for the OTel Collector to process an exported batch of data. This timeout spans the entire call: resolving DNS, connecting, writing the request body, server processing, and reading the response body. If the call requires redirects or retries all must complete within one timeout period.",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"defaultValue": "10s",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.timeout",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "management.otlp.logging.transport",
|
||||
"type": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport",
|
||||
"description": "Transport used to send the logs.",
|
||||
"sourceType": "org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportProperties",
|
||||
"defaultValue": "http",
|
||||
"deprecation": {
|
||||
"replacement": "management.opentelemetry.logging.export.transport",
|
||||
"level": "error"
|
||||
}
|
||||
}
|
||||
],
|
||||
"hints": []
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportAutoConfiguration
|
||||
org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -32,26 +32,25 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.OpenTelemetryLoggingAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link OtlpLoggingAutoConfiguration}.
|
||||
* Integration tests for {@link OpenTelemetryLoggingExportAutoConfiguration}.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
class OtlpLoggingAutoConfigurationIntegrationTests {
|
||||
class OpenTelemetryLoggingExportAutoConfigurationIntegrationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.application.name=otlp-logs-test",
|
||||
"management.otlp.logging.headers.Authorization=Bearer my-token")
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class,
|
||||
OpenTelemetryLoggingAutoConfiguration.class, OtlpLoggingAutoConfiguration.class));
|
||||
"management.opentelemetry.logging.export.headers.Authorization=Bearer my-token")
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class,
|
||||
OpenTelemetryLoggingExportAutoConfiguration.class));
|
||||
|
||||
private final MockWebServer mockWebServer = new MockWebServer();
|
||||
|
||||
|
@ -69,7 +68,7 @@ class OtlpLoggingAutoConfigurationIntegrationTests {
|
|||
void httpLogRecordExporterShouldUseProtobufAndNoCompressionByDefault() {
|
||||
this.mockWebServer.enqueue(new MockResponse());
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.otlp.logging.endpoint=http://localhost:%d/v1/logs"
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:%d/v1/logs"
|
||||
.formatted(this.mockWebServer.getPort()))
|
||||
.run((context) -> {
|
||||
logMessage(context);
|
||||
|
@ -89,8 +88,8 @@ class OtlpLoggingAutoConfigurationIntegrationTests {
|
|||
void httpLogRecordExporterCanBeConfiguredToUseGzipCompression() {
|
||||
this.mockWebServer.enqueue(new MockResponse());
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.otlp.logging.endpoint=http://localhost:%d/v1/logs"
|
||||
.formatted(this.mockWebServer.getPort()), "management.otlp.logging.compression=gzip")
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:%d/v1/logs"
|
||||
.formatted(this.mockWebServer.getPort()), "management.opentelemetry.logging.export.compression=gzip")
|
||||
.run((context) -> {
|
||||
logMessage(context);
|
||||
RecordedRequest request = this.mockWebServer.takeRequest(10, TimeUnit.SECONDS);
|
|
@ -14,13 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.logging.otlp;
|
||||
package org.springframework.boot.opentelemetry.actuate.autoconfigure.logging;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.opentelemetry.api.metrics.MeterProvider;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
|
||||
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
|
||||
import okhttp3.HttpUrl;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
|
@ -28,8 +30,12 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConfigurations.ConnectionDetails.PropertiesOtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetailsConfiguration.PropertiesOpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration;
|
||||
import org.springframework.boot.opentelemetry.autoconfigure.SdkLoggerProviderBuilderCustomizer;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -38,87 +44,101 @@ import org.springframework.context.annotation.Configuration;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OtlpLoggingAutoConfiguration}.
|
||||
* Tests for {@link OpenTelemetryLoggingExportAutoConfiguration}.
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class OtlpLoggingAutoConfigurationTests {
|
||||
class OpenTelemetryLoggingExportAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class));
|
||||
private final ApplicationContextRunner contextRunner;
|
||||
|
||||
@Test
|
||||
void shouldNotSupplyBeansIfPropertyIsNotSet() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OtlpLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(OtlpHttpLogRecordExporter.class);
|
||||
});
|
||||
OpenTelemetryLoggingExportAutoConfigurationTests() {
|
||||
this.contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations
|
||||
.of(OpenTelemetrySdkAutoConfiguration.class, OpenTelemetryLoggingExportAutoConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupplyBeans() {
|
||||
this.contextRunner.withPropertyValues("management.otlp.logging.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class);
|
||||
OtlpLoggingConnectionDetails connectionDetails = context.getBean(OtlpLoggingConnectionDetails.class);
|
||||
assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/logs");
|
||||
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class)
|
||||
.hasSingleBean(LogRecordExporter.class);
|
||||
});
|
||||
void registeredInAutoConfigurationImports() {
|
||||
assertThat(ImportCandidates.load(AutoConfiguration.class, null).getCandidates())
|
||||
.contains(OpenTelemetryLoggingExportAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "io.opentelemetry.sdk.logs", "io.opentelemetry.api",
|
||||
"io.opentelemetry.exporter.otlp.http.logs" })
|
||||
void shouldNotSupplyBeansIfDependencyIsMissing(String packageName) {
|
||||
void whenOpenTelemetryIsNotOnClasspathDoesNotProvideBeans(String packageName) {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(packageName)).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OtlpLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetryLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(OtlpHttpLogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffWhenLoggingExportPropertyIsNotEnabled() {
|
||||
void whenHasEndpointPropertyProvidesBeans() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetryLoggingConnectionDetails.class);
|
||||
OpenTelemetryLoggingConnectionDetails connectionDetails = context
|
||||
.getBean(OpenTelemetryLoggingConnectionDetails.class);
|
||||
assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/logs");
|
||||
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(LogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasNoEndpointPropertyDoesNotProvideBeans() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetryLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(OtlpHttpLogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenOpenTelemetryLoggingExportEnabledPropertyIsFalseProvidesExpectedBeans() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.opentelemetry.logging.export.enabled=false",
|
||||
"management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetryLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(LogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenLoggingExportEnabledPropertyIsFalseNoProvideExpectedBeans() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.logging.export.enabled=false",
|
||||
"management.otlp.logging.endpoint=http://localhost:4318/v1/logs")
|
||||
"management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetryLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(LogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffWhenOtlpLoggingExportPropertyIsNotEnabled() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.otlp.logging.export.enabled=false",
|
||||
"management.otlp.logging.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class);
|
||||
assertThat(context).doesNotHaveBean(LogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffWhenCustomHttpExporterIsDefined() {
|
||||
void whenHasCustomHttpExporterDoesNotProvideExporterBean() {
|
||||
this.contextRunner.withUserConfiguration(CustomHttpExporterConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("customOtlpHttpLogRecordExporter")
|
||||
.hasSingleBean(LogRecordExporter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffWhenCustomGrpcExporterIsDefined() {
|
||||
void whenHasCustomGrpcExporterDoesNotProvideExporterBean() {
|
||||
this.contextRunner.withUserConfiguration(CustomGrpcExporterConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("customOtlpGrpcLogRecordExporter")
|
||||
.hasSingleBean(LogRecordExporter.class));
|
||||
}
|
||||
// FIXME
|
||||
|
||||
@Test
|
||||
void shouldBackOffWhenCustomOtlpLoggingConnectionDetailsIsDefined() {
|
||||
this.contextRunner.withUserConfiguration(CustomOtlpLoggingConnectionDetails.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class)
|
||||
.doesNotHaveBean(PropertiesOtlpLoggingConnectionDetails.class);
|
||||
void whenHasCustomLoggingConnectionDetailsDoesNotProvideExporterBean() {
|
||||
this.contextRunner.withUserConfiguration(CustomOtlpLoggingConnectionDetailsConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetryLoggingConnectionDetails.class)
|
||||
.doesNotHaveBean(PropertiesOpenTelemetryLoggingConnectionDetails.class);
|
||||
OtlpHttpLogRecordExporter otlpHttpLogRecordExporter = context.getBean(OtlpHttpLogRecordExporter.class);
|
||||
assertThat(otlpHttpLogRecordExporter).extracting("delegate.httpSender.url")
|
||||
.isEqualTo(HttpUrl.get("https://otel.example.com/v1/logs"));
|
||||
|
@ -126,43 +146,44 @@ class OtlpLoggingAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void shouldUseHttpExporterIfTransportIsNotSet() {
|
||||
this.contextRunner.withPropertyValues("management.otlp.logging.endpoint=http://localhost:4318/v1/logs")
|
||||
void whenHasNoTransportPropertySetUsesHttpExporter() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class)
|
||||
.hasSingleBean(LogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(LogRecordExporter.class);
|
||||
assertThat(context).doesNotHaveBean(OtlpGrpcLogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseHttpExporterIfTransportIsSetToHttp() {
|
||||
void whenHasTransportPropertySetToHttpUsesHttpExporter() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.otlp.logging.endpoint=http://localhost:4318/v1/logs",
|
||||
"management.otlp.logging.transport=http")
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs",
|
||||
"management.opentelemetry.logging.export.transport=http")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class)
|
||||
.hasSingleBean(LogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(LogRecordExporter.class);
|
||||
assertThat(context).doesNotHaveBean(OtlpGrpcLogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseGrpcExporterIfTransportIsSetToGrpc() {
|
||||
void whenHasTransportPropertySetToGrpcUsesGrpcExporter() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.otlp.logging.endpoint=http://localhost:4318/v1/logs",
|
||||
"management.otlp.logging.transport=grpc")
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs",
|
||||
"management.opentelemetry.logging.export.transport=grpc")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OtlpGrpcLogRecordExporter.class)
|
||||
.hasSingleBean(LogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(OtlpGrpcLogRecordExporter.class);
|
||||
assertThat(context).hasSingleBean(LogRecordExporter.class);
|
||||
assertThat(context).doesNotHaveBean(OtlpHttpLogRecordExporter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpShouldUseMeterProviderIfSet() {
|
||||
void whenHasMeterProviderBeanAddsItToHttpExporter() {
|
||||
this.contextRunner.withUserConfiguration(MeterProviderConfiguration.class)
|
||||
.withPropertyValues("management.otlp.logging.endpoint=http://localhost:4318/v1/logs")
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs")
|
||||
.run((context) -> {
|
||||
OtlpHttpLogRecordExporter otlpHttpLogRecordExporter = context.getBean(OtlpHttpLogRecordExporter.class);
|
||||
assertThat(otlpHttpLogRecordExporter.toBuilder())
|
||||
|
@ -173,10 +194,10 @@ class OtlpLoggingAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void grpcShouldUseMeterProviderIfSet() {
|
||||
void whenHasMeterProviderBeanAddsItToGrpcExporter() {
|
||||
this.contextRunner.withUserConfiguration(MeterProviderConfiguration.class)
|
||||
.withPropertyValues("management.otlp.logging.endpoint=http://localhost:4318/v1/logs",
|
||||
"management.otlp.logging.transport=grpc")
|
||||
.withPropertyValues("management.opentelemetry.logging.export.endpoint=http://localhost:4318/v1/logs",
|
||||
"management.opentelemetry.logging.export.transport=grpc")
|
||||
.run((context) -> {
|
||||
OtlpGrpcLogRecordExporter otlpGrpcLogRecordExporter = context.getBean(OtlpGrpcLogRecordExporter.class);
|
||||
assertThat(otlpGrpcLogRecordExporter.toBuilder())
|
||||
|
@ -186,6 +207,36 @@ class OtlpLoggingAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class MultipleSdkLoggerProviderBuilderCustomizersConfig {
|
||||
|
||||
@Bean
|
||||
public SdkLoggerProviderBuilderCustomizer customSdkLoggerProviderBuilderCustomizer1() {
|
||||
return new NoopSdkLoggerProviderBuilderCustomizer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SdkLoggerProviderBuilderCustomizer customSdkLoggerProviderBuilderCustomizer2() {
|
||||
return new NoopSdkLoggerProviderBuilderCustomizer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopSdkLoggerProviderBuilderCustomizer implements SdkLoggerProviderBuilderCustomizer {
|
||||
|
||||
final AtomicInteger called = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void customize(SdkLoggerProviderBuilder builder) {
|
||||
this.called.incrementAndGet();
|
||||
}
|
||||
|
||||
int called() {
|
||||
return this.called.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
private static final class MeterProviderConfiguration {
|
||||
|
||||
|
@ -219,10 +270,10 @@ class OtlpLoggingAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
private static final class CustomOtlpLoggingConnectionDetails {
|
||||
private static final class CustomOtlpLoggingConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
OtlpLoggingConnectionDetails customOtlpLoggingConnectionDetails() {
|
||||
OpenTelemetryLoggingConnectionDetails customOtlpLoggingConnectionDetails() {
|
||||
return (transport) -> "https://otel.example.com/v1/logs";
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
package org.springframework.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
@ -99,7 +99,7 @@ class OpenTelemetryResourceAttributesTests {
|
|||
@SuppressWarnings("unchecked")
|
||||
void systemGetEnvShouldBeUsedAsDefaultEnvFunction() {
|
||||
OpenTelemetryResourceAttributes attributes = new OpenTelemetryResourceAttributes(this.environment, null);
|
||||
Function<String, String> getEnv = assertThat(attributes).extracting("getEnv")
|
||||
Function<String, String> getEnv = assertThat(attributes).extracting("systemEnvironment")
|
||||
.asInstanceOf(InstanceOfAssertFactories.type(Function.class))
|
||||
.actual();
|
||||
System.getenv().forEach((key, value) -> assertThat(getEnv.apply(key)).isEqualTo(value));
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.boot.opentelemetry.autoconfigure;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||
import io.opentelemetry.sdk.logs.LogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||
import io.opentelemetry.sdk.logs.data.LogRecordData;
|
||||
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
|
||||
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link OpenTelemetrySdkAutoConfiguration}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Toshiaki Maki
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OpenTelemetrySdkAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void registeredInAutoConfigurationImports() {
|
||||
assertThat(ImportCandidates.load(AutoConfiguration.class, null).getCandidates())
|
||||
.contains(OpenTelemetrySdkAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void providesBeans() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "io.opentelemetry", "io.opentelemetry.api" })
|
||||
void whenOpenTelemetryIsNotOnClasspathDoesNotProvideBeans(String packageName) {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(packageName)).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).doesNotHaveBean(Resource.class);
|
||||
assertThat(context).doesNotHaveBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).doesNotHaveBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenOpenTelemetryLogsIsNotOnClasspathDoesNotProvideBeans() {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader("io.opentelemetry.sdk.logs")).run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
assertThat(context).doesNotHaveBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).doesNotHaveBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasUserSuppliedBeansDoesNotProvideBeans() {
|
||||
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetry.class);
|
||||
assertThat(context).hasBean("customOpenTelemetry");
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
assertThat(context).hasBean("customResource");
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).hasBean("customBatchLogRecordProcessor").hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).hasSingleBean(LogRecordProcessor.class);
|
||||
assertThat(context).hasBean("customSdkLoggerProvider").hasSingleBean(SdkLoggerProvider.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasApplicationNamePropertyProvidesServiceNameResourceAttribute() {
|
||||
this.contextRunner.withPropertyValues("spring.application.name=my-application").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("service.name"), "my-application"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasApplicationGroupPropertyProvidesServiceGroupResourceAttribute() {
|
||||
this.contextRunner.withPropertyValues("spring.application.group=my-group").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("service.group"), "my-group"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasApplicationGroupPropertyProvidesServiceNamespaceResourceAttribute() {
|
||||
this.contextRunner.withPropertyValues("spring.application.group=my-group").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).containsEntry(AttributeKey.stringKey("service.namespace"),
|
||||
"my-group");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasNoApplicationGroupPropertyProvidesNoServiceGroupResourceAttribute() {
|
||||
this.contextRunner.run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("service.group"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasNoApplicationGroupPropertyProvidesNoServiceNamespaceResourceAttribute() {
|
||||
this.contextRunner.run(((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("service.namespace"));
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasNoApplicationNamePropertyProvidesDefaultApplicationName() {
|
||||
this.contextRunner.run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("service.name"), "unknown_service"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasResourceAttributesPropertyProvidesResourceAttributes() {
|
||||
this.contextRunner.withPropertyValues("management.opentelemetry.resource-attributes.region=us-west")
|
||||
.run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(AttributeKey.stringKey("region"), "us-west"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasSdkTracerProviderBeanProvidesTracerProvider() {
|
||||
this.contextRunner.withBean(SdkTracerProvider.class, () -> SdkTracerProvider.builder().build())
|
||||
.run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getTracerProvider()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasContextPropagatorsBeanProvidesPropagators() {
|
||||
this.contextRunner.withBean(ContextPropagators.class, ContextPropagators::noop).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getPropagators()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasSdkLoggerProviderBeanProvidesLogsBridge() {
|
||||
this.contextRunner.withBean(SdkLoggerProvider.class, () -> SdkLoggerProvider.builder().build())
|
||||
.run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getLogsBridge()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasSdkMeterProviderProvidesMeterProvider() {
|
||||
this.contextRunner.withBean(SdkMeterProvider.class, () -> SdkMeterProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getMeterProvider()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasMultipleLogRecordExportersProvidesBatchLogRecordProcessor() {
|
||||
this.contextRunner.withUserConfiguration(MultipleLogRecordExportersConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context.getBeansOfType(LogRecordExporter.class)).hasSize(2);
|
||||
assertThat(context).hasBean("customLogRecordExporter1");
|
||||
assertThat(context).hasBean("customLogRecordExporter2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasMultipleLogRecordProcessorsStillProvidesBatchLogRecordProcessor() {
|
||||
this.contextRunner.withUserConfiguration(MultipleLogRecordProcessorsConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class);
|
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class);
|
||||
assertThat(context.getBeansOfType(LogRecordProcessor.class)).hasSize(3);
|
||||
assertThat(context).hasBean("openTelemetryBatchLogRecordProcessor");
|
||||
assertThat(context).hasBean("customLogRecordProcessor1");
|
||||
assertThat(context).hasBean("customLogRecordProcessor2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasMultipleSdkLoggerProviderBuilderCustomizersCallsCustomizeMethod() {
|
||||
this.contextRunner.withUserConfiguration(MultipleSdkLoggerProviderBuilderCustomizersConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class);
|
||||
assertThat(context.getBeansOfType(SdkLoggerProviderBuilderCustomizer.class)).hasSize(2);
|
||||
assertThat(context).hasBean("customSdkLoggerProviderBuilderCustomizer1");
|
||||
assertThat(context).hasBean("customSdkLoggerProviderBuilderCustomizer2");
|
||||
assertThat(context
|
||||
.getBean("customSdkLoggerProviderBuilderCustomizer1", NoopSdkLoggerProviderBuilderCustomizer.class)
|
||||
.called()).isEqualTo(1);
|
||||
assertThat(context
|
||||
.getBean("customSdkLoggerProviderBuilderCustomizer2", NoopSdkLoggerProviderBuilderCustomizer.class)
|
||||
.called()).isEqualTo(1);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class UserConfiguration {
|
||||
|
||||
@Bean
|
||||
OpenTelemetry customOpenTelemetry() {
|
||||
return mock(OpenTelemetry.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Resource customResource() {
|
||||
return Resource.getDefault();
|
||||
}
|
||||
|
||||
@Bean
|
||||
BatchLogRecordProcessor customBatchLogRecordProcessor() {
|
||||
return BatchLogRecordProcessor.builder(new NoopLogRecordExporter()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SdkLoggerProvider customSdkLoggerProvider() {
|
||||
return SdkLoggerProvider.builder().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleLogRecordExportersConfiguration {
|
||||
|
||||
@Bean
|
||||
LogRecordExporter customLogRecordExporter1() {
|
||||
return new NoopLogRecordExporter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
LogRecordExporter customLogRecordExporter2() {
|
||||
return new NoopLogRecordExporter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleLogRecordProcessorsConfiguration {
|
||||
|
||||
@Bean
|
||||
LogRecordProcessor customLogRecordProcessor1() {
|
||||
return new NoopLogRecordProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
LogRecordProcessor customLogRecordProcessor2() {
|
||||
return new NoopLogRecordProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleSdkLoggerProviderBuilderCustomizersConfiguration {
|
||||
|
||||
@Bean
|
||||
SdkLoggerProviderBuilderCustomizer customSdkLoggerProviderBuilderCustomizer1() {
|
||||
return new NoopSdkLoggerProviderBuilderCustomizer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SdkLoggerProviderBuilderCustomizer customSdkLoggerProviderBuilderCustomizer2() {
|
||||
return new NoopSdkLoggerProviderBuilderCustomizer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopLogRecordExporter implements LogRecordExporter {
|
||||
|
||||
@Override
|
||||
public CompletableResultCode export(Collection<LogRecordData> logs) {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode flush() {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode shutdown() {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopLogRecordProcessor implements LogRecordProcessor {
|
||||
|
||||
@Override
|
||||
public void onEmit(Context context, ReadWriteLogRecord logRecord) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopSdkLoggerProviderBuilderCustomizer implements SdkLoggerProviderBuilderCustomizer {
|
||||
|
||||
final AtomicInteger called = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void customize(SdkLoggerProviderBuilder builder) {
|
||||
this.called.incrementAndGet();
|
||||
}
|
||||
|
||||
int called() {
|
||||
return this.called.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -44,6 +44,7 @@ dependencies {
|
|||
dockerTestImplementation("org.testcontainers:junit-jupiter")
|
||||
|
||||
optional(project(":spring-boot-project:spring-boot-actuator-autoconfigure-all"))
|
||||
optional(project(":spring-boot-project:spring-boot-opentelemetry"))
|
||||
optional(project(":spring-boot-project:spring-boot-tx"))
|
||||
optional(project(":spring-boot-project:spring-boot-zipkin"))
|
||||
optional("org.springframework:spring-test")
|
||||
|
|
|
@ -22,10 +22,10 @@ import org.testcontainers.junit.jupiter.Container;
|
|||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportAutoConfiguration;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -47,7 +47,7 @@ class GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTes
|
|||
static final LgtmStackContainer container = TestImage.container(LgtmStackContainer.class);
|
||||
|
||||
@Autowired
|
||||
private OtlpLoggingConnectionDetails connectionDetails;
|
||||
private OpenTelemetryLoggingConnectionDetails connectionDetails;
|
||||
|
||||
@Test
|
||||
void connectionCanBeMadeToOpenTelemetryContainer() {
|
||||
|
@ -58,7 +58,7 @@ class GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTes
|
|||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportAutoConfiguration(OtlpLoggingAutoConfiguration.class)
|
||||
@ImportAutoConfiguration(OpenTelemetryLoggingExportAutoConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ import org.testcontainers.junit.jupiter.Container;
|
|||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -48,7 +48,7 @@ class OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests {
|
|||
.withExposedPorts(4317, 4318);
|
||||
|
||||
@Autowired
|
||||
private OtlpLoggingConnectionDetails connectionDetails;
|
||||
private OpenTelemetryLoggingConnectionDetails connectionDetails;
|
||||
|
||||
@Test
|
||||
void connectionCanBeMadeToOpenTelemetryContainer() {
|
||||
|
|
|
@ -18,36 +18,36 @@ package org.springframework.boot.testcontainers.service.connection.otlp;
|
|||
|
||||
import org.testcontainers.grafana.LgtmStackContainer;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} to create
|
||||
* {@link OtlpLoggingConnectionDetails} from a
|
||||
* {@link OpenTelemetryLoggingConnectionDetails} from a
|
||||
* {@link ServiceConnection @ServiceConnection}-annotated {@link LgtmStackContainer} using
|
||||
* the {@code "grafana/otel-lgtm"} image.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<LgtmStackContainer, OtlpLoggingConnectionDetails> {
|
||||
extends ContainerConnectionDetailsFactory<LgtmStackContainer, OpenTelemetryLoggingConnectionDetails> {
|
||||
|
||||
GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME,
|
||||
"org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration");
|
||||
"org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportAutoConfiguration");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OtlpLoggingConnectionDetails getContainerConnectionDetails(
|
||||
protected OpenTelemetryLoggingConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<LgtmStackContainer> source) {
|
||||
return new OpenTelemetryLoggingContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
private static final class OpenTelemetryLoggingContainerConnectionDetails
|
||||
extends ContainerConnectionDetails<LgtmStackContainer> implements OtlpLoggingConnectionDetails {
|
||||
extends ContainerConnectionDetails<LgtmStackContainer> implements OpenTelemetryLoggingConnectionDetails {
|
||||
|
||||
private OpenTelemetryLoggingContainerConnectionDetails(ContainerConnectionSource<LgtmStackContainer> source) {
|
||||
super(source);
|
||||
|
|
|
@ -19,15 +19,15 @@ package org.springframework.boot.testcontainers.service.connection.otlp;
|
|||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
|
||||
import org.springframework.boot.actuate.autoconfigure.logging.otlp.Transport;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingConnectionDetails;
|
||||
import org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.Transport;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} to create
|
||||
* {@link OtlpLoggingConnectionDetails} from a
|
||||
* {@link OpenTelemetryLoggingConnectionDetails} from a
|
||||
* {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using
|
||||
* the {@code "otel/opentelemetry-collector-contrib"} image.
|
||||
*
|
||||
|
@ -35,7 +35,7 @@ import org.springframework.boot.testcontainers.service.connection.ServiceConnect
|
|||
* @author Moritz Halbritter
|
||||
*/
|
||||
class OpenTelemetryLoggingContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<Container<?>, OtlpLoggingConnectionDetails> {
|
||||
extends ContainerConnectionDetailsFactory<Container<?>, OpenTelemetryLoggingConnectionDetails> {
|
||||
|
||||
private static final int OTLP_GRPC_PORT = 4317;
|
||||
|
||||
|
@ -43,17 +43,17 @@ class OpenTelemetryLoggingContainerConnectionDetailsFactory
|
|||
|
||||
OpenTelemetryLoggingContainerConnectionDetailsFactory() {
|
||||
super("otel/opentelemetry-collector-contrib",
|
||||
"org.springframework.boot.actuate.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration");
|
||||
"org.springframework.boot.opentelemetry.actuate.autoconfigure.logging.OpenTelemetryLoggingExportAutoConfiguration");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OtlpLoggingConnectionDetails getContainerConnectionDetails(
|
||||
protected OpenTelemetryLoggingConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<Container<?>> source) {
|
||||
return new OpenTelemetryLoggingContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
private static final class OpenTelemetryLoggingContainerConnectionDetails
|
||||
extends ContainerConnectionDetails<Container<?>> implements OtlpLoggingConnectionDetails {
|
||||
extends ContainerConnectionDetails<Container<?>> implements OpenTelemetryLoggingConnectionDetails {
|
||||
|
||||
private OpenTelemetryLoggingContainerConnectionDetails(ContainerConnectionSource<Container<?>> source) {
|
||||
super(source);
|
||||
|
|
Loading…
Reference in New Issue