diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderApplicationListener.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderApplicationListener.java new file mode 100644 index 000000000000..fecc5a952513 --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderApplicationListener.java @@ -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> appenderIterator = logger.iteratorForAppenders(); + while (appenderIterator.hasNext()) { + Appender 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 + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 90cd162ad80c..4c9f14592f45 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -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 diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/testLogbackAppender/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/testLogbackAppender/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderTest.java index 3aae0d650142..01c9a23541ae 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/testLogbackAppender/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/testLogbackAppender/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderTest.java @@ -60,7 +60,9 @@ void shouldInitializeAppender() { 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"); diff --git a/smoke-tests-otel-starter/src/main/resources/logback.xml b/smoke-tests-otel-starter/src/main/resources/logback.xml deleted file mode 100644 index 9a719b2d72c9..000000000000 --- a/smoke-tests-otel-starter/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - -