Skip to content

Commit d20da99

Browse files
authored
Log4j and Logback appenders opt-in to using GlobalOpenTelemetry (open-telemetry#8791)
1 parent a1f6917 commit d20da99

File tree

10 files changed

+178
-159
lines changed

10 files changed

+178
-159
lines changed

instrumentation/log4j/log4j-appender-2.17/library/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,23 @@ The following demonstrates how you might configure the appender in your `log4j.x
5656

5757
In this example Log4j2 log events will be sent to both the console appender and
5858
the `OpenTelemetryAppender`.
59+
60+
In order to function, `OpenTelemetryAppender` needs access to an `OpenTelemetry` instance. This must
61+
be set programmatically during application startup as follows:
62+
63+
```java
64+
import io.opentelemetry.instrumentation.log4j.appender.v2_17.OpenTelemetryAppender;
65+
import io.opentelemetry.sdk.OpenTelemetrySdk;
66+
67+
public class Application {
68+
69+
public static void main(String[] args) {
70+
OpenTelemetrySdk openTelemetrySdk = // Configure OpenTelemetrySdk
71+
72+
// Find OpenTelemetryAppender in log4j configuration and install openTelemetrySdk
73+
OpenTelemetryAppender.install(openTelemetrySdk);
74+
75+
// ... proceed with application
76+
}
77+
}
78+
```

instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import static java.util.Collections.emptyList;
99

1010
import com.google.errorprone.annotations.CanIgnoreReturnValue;
11-
import io.opentelemetry.api.GlobalOpenTelemetry;
1211
import io.opentelemetry.api.OpenTelemetry;
1312
import io.opentelemetry.api.logs.LogRecordBuilder;
1413
import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.ContextDataAccessor;
@@ -20,13 +19,16 @@
2019
import java.util.function.BiConsumer;
2120
import java.util.stream.Collectors;
2221
import javax.annotation.Nullable;
22+
import org.apache.logging.log4j.LogManager;
2323
import org.apache.logging.log4j.ThreadContext;
2424
import org.apache.logging.log4j.core.Appender;
2525
import org.apache.logging.log4j.core.Core;
2626
import org.apache.logging.log4j.core.Filter;
2727
import org.apache.logging.log4j.core.Layout;
2828
import org.apache.logging.log4j.core.LogEvent;
29+
import org.apache.logging.log4j.core.LoggerContext;
2930
import org.apache.logging.log4j.core.appender.AbstractAppender;
31+
import org.apache.logging.log4j.core.config.Configuration;
3032
import org.apache.logging.log4j.core.config.Property;
3133
import org.apache.logging.log4j.core.config.plugins.Plugin;
3234
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
@@ -44,7 +46,29 @@ public class OpenTelemetryAppender extends AbstractAppender {
4446
static final String PLUGIN_NAME = "OpenTelemetry";
4547

4648
private final LogEventMapper<ReadOnlyStringMap> mapper;
47-
@Nullable private OpenTelemetry openTelemetry;
49+
private OpenTelemetry openTelemetry;
50+
51+
/**
52+
* Installs the {@code openTelemetry} instance on any {@link OpenTelemetryAppender}s identified in
53+
* the {@link LoggerContext}.
54+
*/
55+
public static void install(OpenTelemetry openTelemetry) {
56+
org.apache.logging.log4j.spi.LoggerContext loggerContextSpi = LogManager.getContext(false);
57+
if (!(loggerContextSpi instanceof LoggerContext)) {
58+
return;
59+
}
60+
LoggerContext loggerContext = (LoggerContext) loggerContextSpi;
61+
Configuration config = loggerContext.getConfiguration();
62+
config
63+
.getAppenders()
64+
.values()
65+
.forEach(
66+
appender -> {
67+
if (appender instanceof OpenTelemetryAppender) {
68+
((OpenTelemetryAppender) appender).setOpenTelemetry(openTelemetry);
69+
}
70+
});
71+
}
4872

4973
@PluginBuilderFactory
5074
public static <B extends Builder<B>> B builder() {
@@ -97,6 +121,7 @@ public B setCaptureContextDataAttributes(String captureContextDataAttributes) {
97121
return asBuilder();
98122
}
99123

124+
/** Configures the {@link OpenTelemetry} used to append logs. */
100125
@CanIgnoreReturnValue
101126
public B setOpenTelemetry(OpenTelemetry openTelemetry) {
102127
this.openTelemetry = openTelemetry;
@@ -105,6 +130,10 @@ public B setOpenTelemetry(OpenTelemetry openTelemetry) {
105130

106131
@Override
107132
public OpenTelemetryAppender build() {
133+
OpenTelemetry openTelemetry = this.openTelemetry;
134+
if (openTelemetry == null) {
135+
openTelemetry = OpenTelemetry.noop();
136+
}
108137
return new OpenTelemetryAppender(
109138
getName(),
110139
getLayout(),
@@ -152,14 +181,14 @@ private static List<String> splitAndFilterBlanksAndNulls(String value) {
152181
.collect(Collectors.toList());
153182
}
154183

184+
/**
185+
* Configures the {@link OpenTelemetry} used to append logs. This MUST be called for the appender
186+
* to function. See {@link #install(OpenTelemetry)} for simple installation option.
187+
*/
155188
public void setOpenTelemetry(OpenTelemetry openTelemetry) {
156189
this.openTelemetry = openTelemetry;
157190
}
158191

159-
private OpenTelemetry getOpenTelemetry() {
160-
return openTelemetry == null ? GlobalOpenTelemetry.get() : openTelemetry;
161-
}
162-
163192
@Override
164193
public void append(LogEvent event) {
165194
String instrumentationName = event.getLoggerName();
@@ -168,7 +197,7 @@ public void append(LogEvent event) {
168197
}
169198

170199
LogRecordBuilder builder =
171-
getOpenTelemetry()
200+
this.openTelemetry
172201
.getLogsBridge()
173202
.loggerBuilder(instrumentationName)
174203
.build()

instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppenderConfigTest.java

Lines changed: 0 additions & 57 deletions
This file was deleted.

instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppenderConfigWithOpenTelemetryTest.java

Lines changed: 0 additions & 78 deletions
This file was deleted.
Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
import io.opentelemetry.api.trace.Span;
1818
import io.opentelemetry.api.trace.SpanContext;
1919
import io.opentelemetry.context.Scope;
20+
import io.opentelemetry.sdk.OpenTelemetrySdk;
2021
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
22+
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
2123
import io.opentelemetry.sdk.logs.data.LogRecordData;
24+
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
2225
import io.opentelemetry.sdk.resources.Resource;
2326
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
2427
import io.opentelemetry.sdk.trace.SdkTracerProvider;
@@ -30,26 +33,71 @@
3033
import org.apache.logging.log4j.Marker;
3134
import org.apache.logging.log4j.MarkerManager;
3235
import org.apache.logging.log4j.ThreadContext;
36+
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
37+
import org.apache.logging.log4j.message.FormattedMessage;
3338
import org.apache.logging.log4j.message.StringMapMessage;
3439
import org.apache.logging.log4j.message.StructuredDataMessage;
40+
import org.junit.jupiter.api.AfterAll;
41+
import org.junit.jupiter.api.BeforeAll;
3542
import org.junit.jupiter.api.BeforeEach;
3643
import org.junit.jupiter.api.Test;
3744

38-
abstract class OpenTelemetryAppenderConfigTestBase {
45+
class OpenTelemetryAppenderTest {
3946

40-
static final Logger logger = LogManager.getLogger("TestLogger");
47+
private static final Logger logger = LogManager.getLogger("TestLogger");
4148

42-
static InMemoryLogRecordExporter logRecordExporter;
43-
static Resource resource;
44-
static InstrumentationScopeInfo instrumentationScopeInfo;
45-
static OpenTelemetry openTelemetry;
49+
private static InMemoryLogRecordExporter logRecordExporter;
50+
private static Resource resource;
51+
private static InstrumentationScopeInfo instrumentationScopeInfo;
52+
private static OpenTelemetry openTelemetry;
53+
54+
@BeforeAll
55+
static void setupAll() {
56+
logRecordExporter = InMemoryLogRecordExporter.create();
57+
resource = Resource.getDefault();
58+
instrumentationScopeInfo = InstrumentationScopeInfo.create("TestLogger");
59+
60+
SdkLoggerProvider loggerProvider =
61+
SdkLoggerProvider.builder()
62+
.setResource(resource)
63+
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter))
64+
.build();
65+
66+
openTelemetry = OpenTelemetrySdk.builder().setLoggerProvider(loggerProvider).build();
67+
OpenTelemetryAppender.install(openTelemetry);
68+
}
4669

4770
@BeforeEach
4871
void setup() {
4972
logRecordExporter.reset();
5073
ThreadContext.clearAll();
5174
}
5275

76+
@AfterAll
77+
static void cleanupAll() {
78+
// This is to make sure that other test classes don't have issues with the logger provider set
79+
OpenTelemetryAppender.install(null);
80+
}
81+
82+
@Test
83+
void initializeWithBuilder() {
84+
OpenTelemetryAppender appender =
85+
OpenTelemetryAppender.builder()
86+
.setName("OpenTelemetryAppender")
87+
.setOpenTelemetry(openTelemetry)
88+
.build();
89+
appender.start();
90+
91+
appender.append(
92+
Log4jLogEvent.newBuilder()
93+
.setMessage(new FormattedMessage("log message 1", (Object) null))
94+
.build());
95+
96+
List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
97+
assertThat(logDataList)
98+
.satisfiesExactly(logRecordData -> assertThat(logDataList.get(0)).hasBody("log message 1"));
99+
}
100+
53101
@Test
54102
void logNoSpan() {
55103
logger.info("log message 1");

instrumentation/logback/logback-appender-1.0/library/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,23 @@ The following demonstrates how you might configure the appender in your `logback
6060

6161
In this example Logback log events will be sent to both the console appender and
6262
the `OpenTelemetryAppender`.
63+
64+
In order to function, `OpenTelemetryAppender` needs access to an `OpenTelemetry` instance. This must
65+
be set programmatically during application startup as follows:
66+
67+
```java
68+
import io.opentelemetry.instrumentation.logback.OpenTelemetryAppender;
69+
import io.opentelemetry.sdk.OpenTelemetrySdk;
70+
71+
public class Application {
72+
73+
public static void main(String[] args) {
74+
OpenTelemetrySdk openTelemetrySdk = // Configure OpenTelemetrySdk
75+
76+
// Find OpenTelemetryAppender in logback configuration and install openTelemetrySdk
77+
OpenTelemetryAppender.install(openTelemetrySdk);
78+
79+
// ... proceed with application
80+
}
81+
}
82+
```

0 commit comments

Comments
 (0)