Skip to content

Commit 8825359

Browse files
authored
Automatic addition of the OTel Logback appender by the OTel starter (#10306)
1 parent 7d544f1 commit 8825359

File tree

4 files changed

+98
-13
lines changed

4 files changed

+98
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging;
7+
8+
import ch.qos.logback.classic.LoggerContext;
9+
import ch.qos.logback.classic.spi.ILoggingEvent;
10+
import ch.qos.logback.core.Appender;
11+
import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender;
12+
import java.util.Iterator;
13+
import org.slf4j.ILoggerFactory;
14+
import org.slf4j.Logger;
15+
import org.slf4j.LoggerFactory;
16+
import org.springframework.boot.SpringApplication;
17+
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
18+
import org.springframework.boot.context.logging.LoggingApplicationListener;
19+
import org.springframework.context.ApplicationContext;
20+
import org.springframework.context.ApplicationEvent;
21+
import org.springframework.context.event.GenericApplicationListener;
22+
import org.springframework.core.ResolvableType;
23+
24+
public class LogbackAppenderApplicationListener implements GenericApplicationListener {
25+
26+
private static final Class<?>[] SOURCE_TYPES = {
27+
SpringApplication.class, ApplicationContext.class
28+
};
29+
30+
private static final Class<?>[] EVENT_TYPES = {ApplicationEnvironmentPreparedEvent.class};
31+
32+
@Override
33+
public boolean supportsSourceType(Class<?> sourceType) {
34+
return isAssignableFrom(sourceType, SOURCE_TYPES);
35+
}
36+
37+
@Override
38+
public boolean supportsEventType(ResolvableType resolvableType) {
39+
return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
40+
}
41+
42+
private static boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
43+
if (type != null) {
44+
for (Class<?> supportedType : supportedTypes) {
45+
if (supportedType.isAssignableFrom(type)) {
46+
return true;
47+
}
48+
}
49+
}
50+
return false;
51+
}
52+
53+
@Override
54+
public void onApplicationEvent(ApplicationEvent event) {
55+
if (event instanceof ApplicationEnvironmentPreparedEvent // Event for which
56+
// org.springframework.boot.context.logging.LoggingApplicationListener
57+
// initializes logging
58+
&& !isOpenTelemetryAppenderAlreadyConfigured()) {
59+
ch.qos.logback.classic.Logger logger =
60+
(ch.qos.logback.classic.Logger)
61+
LoggerFactory.getILoggerFactory().getLogger(Logger.ROOT_LOGGER_NAME);
62+
63+
OpenTelemetryAppender appender = new OpenTelemetryAppender();
64+
appender.start();
65+
logger.addAppender(appender);
66+
}
67+
}
68+
69+
private static boolean isOpenTelemetryAppenderAlreadyConfigured() {
70+
ILoggerFactory loggerFactorySpi = LoggerFactory.getILoggerFactory();
71+
if (!(loggerFactorySpi instanceof LoggerContext)) {
72+
return false;
73+
}
74+
LoggerContext loggerContext = (LoggerContext) loggerFactorySpi;
75+
for (ch.qos.logback.classic.Logger logger : loggerContext.getLoggerList()) {
76+
Iterator<Appender<ILoggingEvent>> appenderIterator = logger.iteratorForAppenders();
77+
while (appenderIterator.hasNext()) {
78+
Appender<ILoggingEvent> appender = appenderIterator.next();
79+
if (appender instanceof OpenTelemetryAppender) {
80+
return true;
81+
}
82+
}
83+
}
84+
return false;
85+
}
86+
87+
@Override
88+
public int getOrder() {
89+
return LoggingApplicationListener.DEFAULT_ORDER + 1; // To execute this listener just after
90+
// org.springframework.boot.context.logging.LoggingApplicationListener
91+
}
92+
}

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

+3
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux.Sp
1515
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
1616
io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationAutoConfiguration,\
1717
io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration
18+
19+
org.springframework.context.ApplicationListener=\
20+
io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.logging.LogbackAppenderApplicationListener

instrumentation/spring/spring-boot-autoconfigure/src/testLogbackAppender/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/logging/LogbackAppenderTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ void shouldInitializeAppender() {
6060
LoggerFactory.getLogger("test").info("test log message");
6161

6262
assertThat(testing.logRecords())
63-
.anySatisfy(
63+
.satisfiesOnlyOnce(
64+
// OTel appender automatically added or from an XML file, it should not
65+
// be added a second time by LogbackAppenderApplicationListener
6466
logRecord -> {
6567
assertThat(logRecord.getInstrumentationScopeInfo().getName()).isEqualTo("test");
6668
assertThat(logRecord.getBody().asString()).contains("test log message");

smoke-tests-otel-starter/src/main/resources/logback.xml

-12
This file was deleted.

0 commit comments

Comments
 (0)