Browse Source
This auto-configuration configures OpenTelemetry logging. The existing OpenTelemetrySdkAutoConfiguration now only configures the OpenTelemetry SDK, but no individual subsystems (logging, metrics, tracing). Closes gh-47963pull/47991/head
23 changed files with 361 additions and 235 deletions
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* 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.logging; |
||||
|
||||
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.ConditionalOnBean; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration; |
||||
import org.springframework.context.annotation.Bean; |
||||
|
||||
/** |
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry logging. |
||||
* |
||||
* @author Moritz Halbritter |
||||
* @since 4.0.0 |
||||
*/ |
||||
@AutoConfiguration(after = OpenTelemetrySdkAutoConfiguration.class) |
||||
@ConditionalOnClass(SdkLoggerProvider.class) |
||||
public final class OpenTelemetryLoggingAutoConfiguration { |
||||
|
||||
OpenTelemetryLoggingAutoConfiguration() { |
||||
} |
||||
|
||||
@Bean |
||||
@ConditionalOnMissingBean |
||||
BatchLogRecordProcessor openTelemetryBatchLogRecordProcessor(ObjectProvider<LogRecordExporter> logRecordExporters) { |
||||
LogRecordExporter exporter = LogRecordExporter.composite(logRecordExporters.orderedStream().toList()); |
||||
return BatchLogRecordProcessor.builder(exporter).build(); |
||||
} |
||||
|
||||
@Bean |
||||
@ConditionalOnMissingBean |
||||
@ConditionalOnBean(Resource.class) |
||||
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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
/** |
||||
* Auto-configuration for exporting logs with OpenTelemetry via OTLP. |
||||
*/ |
||||
@NullMarked |
||||
package org.springframework.boot.opentelemetry.autoconfigure.logging.otlp; |
||||
|
||||
import org.jspecify.annotations.NullMarked; |
||||
@ -1,2 +1,3 @@
@@ -1,2 +1,3 @@
|
||||
org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration |
||||
org.springframework.boot.opentelemetry.autoconfigure.logging.OtlpLoggingAutoConfiguration |
||||
org.springframework.boot.opentelemetry.autoconfigure.logging.OpenTelemetryLoggingAutoConfiguration |
||||
org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration |
||||
|
||||
@ -0,0 +1,228 @@
@@ -0,0 +1,228 @@
|
||||
/* |
||||
* 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.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.springframework.boot.autoconfigure.AutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.AutoConfigurations; |
||||
import org.springframework.boot.context.annotation.ImportCandidates; |
||||
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration; |
||||
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 Moritz Halbritter |
||||
*/ |
||||
class OpenTelemetryLoggingAutoConfigurationTests { |
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() |
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetrySdkAutoConfiguration.class, |
||||
OpenTelemetryLoggingAutoConfiguration.class)); |
||||
|
||||
@Test |
||||
void registeredInAutoConfigurationImports() { |
||||
assertThat(ImportCandidates.load(AutoConfiguration.class, null).getCandidates()) |
||||
.contains(OpenTelemetryLoggingAutoConfiguration.class.getName()); |
||||
} |
||||
|
||||
@Test |
||||
void providesBeans() { |
||||
this.contextRunner.run((context) -> { |
||||
assertThat(context).hasSingleBean(BatchLogRecordProcessor.class); |
||||
assertThat(context).hasSingleBean(SdkLoggerProvider.class); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
void whenOpenTelemetryLogsIsNotOnClasspathDoesNotProvideBeans() { |
||||
this.contextRunner.withClassLoader(new FilteredClassLoader("io.opentelemetry.sdk.logs")).run((context) -> { |
||||
assertThat(context).doesNotHaveBean(BatchLogRecordProcessor.class); |
||||
assertThat(context).doesNotHaveBean(SdkLoggerProvider.class); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
void whenHasUserSuppliedBeansDoesNotProvideBeans() { |
||||
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> { |
||||
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 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 |
||||
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(); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
7
module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OtlpLoggingAutoConfigurationIntegrationTests.java → module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationIntegrationTests.java
7
module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OtlpLoggingAutoConfigurationIntegrationTests.java → module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationIntegrationTests.java
12
module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OtlpLoggingAutoConfigurationTests.java → module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java
12
module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OtlpLoggingAutoConfigurationTests.java → module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java
Loading…
Reference in new issue