Skip to content

Commit

Permalink
Log4j and Logback appenders opt-in to using GlobalOpenTelemetry (#8791)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg committed Jun 29, 2023
1 parent a1f6917 commit d20da99
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 159 deletions.
20 changes: 20 additions & 0 deletions instrumentation/log4j/log4j-appender-2.17/library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,23 @@ The following demonstrates how you might configure the appender in your `log4j.x

In this example Log4j2 log events will be sent to both the console appender and
the `OpenTelemetryAppender`.

In order to function, `OpenTelemetryAppender` needs access to an `OpenTelemetry` instance. This must
be set programmatically during application startup as follows:

```java
import io.opentelemetry.instrumentation.log4j.appender.v2_17.OpenTelemetryAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;

public class Application {

public static void main(String[] args) {
OpenTelemetrySdk openTelemetrySdk = // Configure OpenTelemetrySdk

// Find OpenTelemetryAppender in log4j configuration and install openTelemetrySdk
OpenTelemetryAppender.install(openTelemetrySdk);

// ... proceed with application
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import static java.util.Collections.emptyList;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.ContextDataAccessor;
Expand All @@ -20,13 +19,16 @@
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
Expand All @@ -44,7 +46,29 @@ public class OpenTelemetryAppender extends AbstractAppender {
static final String PLUGIN_NAME = "OpenTelemetry";

private final LogEventMapper<ReadOnlyStringMap> mapper;
@Nullable private OpenTelemetry openTelemetry;
private OpenTelemetry openTelemetry;

/**
* Installs the {@code openTelemetry} instance on any {@link OpenTelemetryAppender}s identified in
* the {@link LoggerContext}.
*/
public static void install(OpenTelemetry openTelemetry) {
org.apache.logging.log4j.spi.LoggerContext loggerContextSpi = LogManager.getContext(false);
if (!(loggerContextSpi instanceof LoggerContext)) {
return;
}
LoggerContext loggerContext = (LoggerContext) loggerContextSpi;
Configuration config = loggerContext.getConfiguration();
config
.getAppenders()
.values()
.forEach(
appender -> {
if (appender instanceof OpenTelemetryAppender) {
((OpenTelemetryAppender) appender).setOpenTelemetry(openTelemetry);
}
});
}

@PluginBuilderFactory
public static <B extends Builder<B>> B builder() {
Expand Down Expand Up @@ -97,6 +121,7 @@ public B setCaptureContextDataAttributes(String captureContextDataAttributes) {
return asBuilder();
}

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

@Override
public OpenTelemetryAppender build() {
OpenTelemetry openTelemetry = this.openTelemetry;
if (openTelemetry == null) {
openTelemetry = OpenTelemetry.noop();
}
return new OpenTelemetryAppender(
getName(),
getLayout(),
Expand Down Expand Up @@ -152,14 +181,14 @@ private static List<String> splitAndFilterBlanksAndNulls(String value) {
.collect(Collectors.toList());
}

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

private OpenTelemetry getOpenTelemetry() {
return openTelemetry == null ? GlobalOpenTelemetry.get() : openTelemetry;
}

@Override
public void append(LogEvent event) {
String instrumentationName = event.getLoggerName();
Expand All @@ -168,7 +197,7 @@ public void append(LogEvent event) {
}

LogRecordBuilder builder =
getOpenTelemetry()
this.openTelemetry
.getLogsBridge()
.loggerBuilder(instrumentationName)
.build()
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
Expand All @@ -30,26 +33,71 @@
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.FormattedMessage;
import org.apache.logging.log4j.message.StringMapMessage;
import org.apache.logging.log4j.message.StructuredDataMessage;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

abstract class OpenTelemetryAppenderConfigTestBase {
class OpenTelemetryAppenderTest {

static final Logger logger = LogManager.getLogger("TestLogger");
private static final Logger logger = LogManager.getLogger("TestLogger");

static InMemoryLogRecordExporter logRecordExporter;
static Resource resource;
static InstrumentationScopeInfo instrumentationScopeInfo;
static OpenTelemetry openTelemetry;
private static InMemoryLogRecordExporter logRecordExporter;
private static Resource resource;
private static InstrumentationScopeInfo instrumentationScopeInfo;
private static OpenTelemetry openTelemetry;

@BeforeAll
static void setupAll() {
logRecordExporter = InMemoryLogRecordExporter.create();
resource = Resource.getDefault();
instrumentationScopeInfo = InstrumentationScopeInfo.create("TestLogger");

SdkLoggerProvider loggerProvider =
SdkLoggerProvider.builder()
.setResource(resource)
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter))
.build();

openTelemetry = OpenTelemetrySdk.builder().setLoggerProvider(loggerProvider).build();
OpenTelemetryAppender.install(openTelemetry);
}

@BeforeEach
void setup() {
logRecordExporter.reset();
ThreadContext.clearAll();
}

@AfterAll
static void cleanupAll() {
// This is to make sure that other test classes don't have issues with the logger provider set
OpenTelemetryAppender.install(null);
}

@Test
void initializeWithBuilder() {
OpenTelemetryAppender appender =
OpenTelemetryAppender.builder()
.setName("OpenTelemetryAppender")
.setOpenTelemetry(openTelemetry)
.build();
appender.start();

appender.append(
Log4jLogEvent.newBuilder()
.setMessage(new FormattedMessage("log message 1", (Object) null))
.build());

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList)
.satisfiesExactly(logRecordData -> assertThat(logDataList.get(0)).hasBody("log message 1"));
}

@Test
void logNoSpan() {
logger.info("log message 1");
Expand Down
20 changes: 20 additions & 0 deletions instrumentation/logback/logback-appender-1.0/library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,23 @@ The following demonstrates how you might configure the appender in your `logback

In this example Logback log events will be sent to both the console appender and
the `OpenTelemetryAppender`.

In order to function, `OpenTelemetryAppender` needs access to an `OpenTelemetry` instance. This must
be set programmatically during application startup as follows:

```java
import io.opentelemetry.instrumentation.logback.OpenTelemetryAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;

public class Application {

public static void main(String[] args) {
OpenTelemetrySdk openTelemetrySdk = // Configure OpenTelemetrySdk

// Find OpenTelemetryAppender in logback configuration and install openTelemetrySdk
OpenTelemetryAppender.install(openTelemetrySdk);

// ... proceed with application
}
}
```
Loading

0 comments on commit d20da99

Please sign in to comment.