Automatic addition of the OTel Logback appender by the OTel starter (#10306)

This commit is contained in:
Jean Bisutti 2024-01-24 17:45:13 +01:00 committed by GitHub
parent 7d544f1476
commit 8825359748
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 98 additions and 13 deletions

View File

@ -0,0 +1,92 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender;
import java.util.Iterator;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.logging.LoggingApplicationListener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
public class LogbackAppenderApplicationListener implements GenericApplicationListener {
private static final Class<?>[] SOURCE_TYPES = {
SpringApplication.class, ApplicationContext.class
};
private static final Class<?>[] EVENT_TYPES = {ApplicationEnvironmentPreparedEvent.class};
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return isAssignableFrom(sourceType, SOURCE_TYPES);
}
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
}
private static boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
if (type != null) {
for (Class<?> supportedType : supportedTypes) {
if (supportedType.isAssignableFrom(type)) {
return true;
}
}
}
return false;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent // Event for which
// org.springframework.boot.context.logging.LoggingApplicationListener
// initializes logging
&& !isOpenTelemetryAppenderAlreadyConfigured()) {
ch.qos.logback.classic.Logger logger =
(ch.qos.logback.classic.Logger)
LoggerFactory.getILoggerFactory().getLogger(Logger.ROOT_LOGGER_NAME);
OpenTelemetryAppender appender = new OpenTelemetryAppender();
appender.start();
logger.addAppender(appender);
}
}
private static boolean isOpenTelemetryAppenderAlreadyConfigured() {
ILoggerFactory loggerFactorySpi = LoggerFactory.getILoggerFactory();
if (!(loggerFactorySpi instanceof LoggerContext)) {
return false;
}
LoggerContext loggerContext = (LoggerContext) loggerFactorySpi;
for (ch.qos.logback.classic.Logger logger : loggerContext.getLoggerList()) {
Iterator<Appender<ILoggingEvent>> appenderIterator = logger.iteratorForAppenders();
while (appenderIterator.hasNext()) {
Appender<ILoggingEvent> appender = appenderIterator.next();
if (appender instanceof OpenTelemetryAppender) {
return true;
}
}
}
return false;
}
@Override
public int getOrder() {
return LoggingApplicationListener.DEFAULT_ORDER + 1; // To execute this listener just after
// org.springframework.boot.context.logging.LoggingApplicationListener
}
}

View File

@ -15,3 +15,6 @@ io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux.Sp
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration
org.springframework.context.ApplicationListener=\
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging.LogbackAppenderApplicationListener

View File

@ -60,7 +60,9 @@ class LogbackAppenderTest {
LoggerFactory.getLogger("test").info("test log message");
assertThat(testing.logRecords())
.anySatisfy(
.satisfiesOnlyOnce(
// OTel appender automatically added or from an XML file, it should not
// be added a second time by LogbackAppenderApplicationListener
logRecord -> {
assertThat(logRecord.getInstrumentationScopeInfo().getName()).isEqualTo("test");
assertThat(logRecord.getBody().asString()).contains("test log message");

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="OpenTelemetry"
class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
</appender>
<root level="INFO">
<appender-ref ref="OpenTelemetry"/>
</root>
</configuration>