Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log4j and Logback appenders opt-in to using GlobalOpenTelemetry #8791

Merged
merged 5 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 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,45 @@ 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 can
be programmatically set during application startup as follows:

```java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
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

// Get Configuration, iterate through appenders, and call setOpenTelemetrySdk(...) on any OpenTelemetryAppender
Configuration config = ((LoggerContext) LogManager.getContext(false)).getConfiguration();
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
config.getAppenders().values().stream()
.filter(a -> a instanceof OpenTelemetryAppender)
.forEach(a -> ((OpenTelemetryAppender) a).setOpenTelemetry(openTelemetrySdk));

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

Optionally, you can configure `OpenTelemetryAppender` to use the `GlobalOpenTelemetry` instance:
```xml
<OpenTelemetry name="OpenTelemetryAppender" useGlobalOpenTelemetry="true" />
```

Note: Setting `useGlobalOpenTelemetry` causes `OpenTelemetryAppender` to
call `GlobalOpenTelemetry.get()`, which initializes `GlobalOpenTelemetry` and prevents future calls
to `GlobalOpenTelemetry.set(...)`. `GlobalOpenTelemetry.get()` will return a noop instance unless
you've opted
into [autoconfigure](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure)
via:

- Add dependency on `io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:OPENTELEMETRY_VERSION`
- Opt into `GlobalOpenTelemetry` autoconfigure by
setting `-Dotel.java.global-autoconfigure.enabled=true`
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
30 changes: 30 additions & 0 deletions instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,34 @@ dependencies {
library("org.apache.logging.log4j:log4j-core:2.17.0")

testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")

testAnnotationProcessor("com.google.auto.service:auto-service")
testCompileOnly("com.google.auto.service:auto-service-annotations")
}

tasks {
val testUseGlobalOpenTelemetry by registering(Test::class) {
filter {
includeTestsMatching("*OpenTelemetryAppenderGlobalTest*")
}
include("**/*OpenTelemetryAppenderGlobalTest.*")
jvmArgs(
"-Dotel.java.global-autoconfigure.enabled=true",
"-Dlog4j.configurationFile=" + project.projectDir.toString() + "/src/test/resources/log4j2-use-global.xml",
"-Dotel.metrics.exporter=none",
"-Dotel.traces.exporter=none",
"-Dotel.logs.exporter=none"
)
}

test {
filter {
excludeTestsMatching("*OpenTelemetryAppenderGlobalTest")
}
}

check {
dependsOn(testUseGlobalOpenTelemetry)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class OpenTelemetryAppender extends AbstractAppender {
static final String PLUGIN_NAME = "OpenTelemetry";

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

@PluginBuilderFactory
public static <B extends Builder<B>> B builder() {
Expand All @@ -58,6 +58,7 @@ public static class Builder<B extends Builder<B>> extends AbstractAppender.Build
@PluginBuilderAttribute private boolean captureMapMessageAttributes;
@PluginBuilderAttribute private boolean captureMarkerAttribute;
@PluginBuilderAttribute private String captureContextDataAttributes;
@PluginBuilderAttribute private boolean useGlobalOpenTelemetry;

@Nullable private OpenTelemetry openTelemetry;

Expand Down Expand Up @@ -97,6 +98,22 @@ public B setCaptureContextDataAttributes(String captureContextDataAttributes) {
return asBuilder();
}

/**
* Configures whether to use {@link io.opentelemetry.api.GlobalOpenTelemetry}. Defaults to
* false. Required for a pure XML / JSON configuration approach. If {@code false}, you MUST
* programmatically call {@link #setOpenTelemetry(OpenTelemetry)} or {@link
* OpenTelemetryAppender#setOpenTelemetry(OpenTelemetry)}.
*/
@CanIgnoreReturnValue
public B setUseGlobalOpenTelemetry(boolean useGlobalOpenTelemetry) {
this.useGlobalOpenTelemetry = useGlobalOpenTelemetry;
return asBuilder();
}

/**
* Configures the {@link OpenTelemetry} used to append logs. Takes precedent over {@link
* #setUseGlobalOpenTelemetry(boolean)}.
*/
@CanIgnoreReturnValue
public B setOpenTelemetry(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
Expand All @@ -105,6 +122,11 @@ public B setOpenTelemetry(OpenTelemetry openTelemetry) {

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

/**
* Configures the {@link OpenTelemetry} used to append logs. You MUST programmatically call if
* {@link Builder#setUseGlobalOpenTelemetry(boolean)} is {@code false}.
*/
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 +190,7 @@ public void append(LogEvent event) {
}

LogRecordBuilder builder =
getOpenTelemetry()
this.openTelemetry
.getLogsBridge()
.loggerBuilder(instrumentationName)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

abstract class OpenTelemetryAppenderConfigTestBase {
abstract class AbstractOpenTelemetryConfigTest {

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

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.log4j.appender.v2_17;

import com.google.auto.service.AutoService;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;

/**
* This test relies on {@link OpenTelemetryAppender} initializing {@link GlobalOpenTelemetry} via
* autoconfigure. {@code log4j2-use-global.xml} opts into using {@link GlobalOpenTelemetry} by
* setting {@link OpenTelemetryAppender.Builder#setUseGlobalOpenTelemetry(boolean)}. The {@code
* testUseGlobalOpenTelemetry} gradle task used to run this test opts into {@link
* GlobalOpenTelemetry} triggering autoconfigure by setting {@code
* -Dotel.java.global-autoconfigure.enabled=true}.
*/
@AutoService(AutoConfigurationCustomizerProvider.class)
public class OpenTelemetryAppenderGlobalTest extends AbstractOpenTelemetryConfigTest
implements AutoConfigurationCustomizerProvider {

@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
logRecordExporter = InMemoryLogRecordExporter.create();
resource = Resource.getDefault();
instrumentationScopeInfo = InstrumentationScopeInfo.create("TestLogger");

autoConfiguration.addLoggerProviderCustomizer(
(sdkLoggerProviderBuilder, configProperties) ->
SdkLoggerProvider.builder()
.setResource(resource)
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

class OpenTelemetryAppenderConfigWithOpenTelemetryTest extends OpenTelemetryAppenderConfigTestBase {
class OpenTelemetryAppenderTest extends AbstractOpenTelemetryConfigTest {

@BeforeAll
static void setupAll() {
logRecordExporter = InMemoryLogRecordExporter.create();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"
packages="com.example.appender,io.opentelemetry.instrumentation.log4j.appender.v2_17">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} traceId: %X{trace_id} spanId: %X{span_id} flags: %X{trace_flags} - %msg%n"/>
</Console>
<OpenTelemetry name="OpenTelemetryAppender" useGlobalOpenTelemetry="true" captureMapMessageAttributes="true" captureMarkerAttribute="true" captureContextDataAttributes="*" />
</Appenders>
<Loggers>
<Logger name="TestLogger" level="All">
<AppenderRef ref="OpenTelemetryAppender" level="All"/>
<AppenderRef ref="Console" level="All"/>
</Logger>
<Root>
<AppenderRef ref="Console" level="All"/>
</Root>
</Loggers>
</Configuration>
43 changes: 43 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,46 @@ 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 can
be programmatically set during application startup as follows:

```java
import ch.qos.logback.classic.LoggerContext;
import io.opentelemetry.instrumentation.logback.OpenTelemetryAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;

public class Application {

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

// Get LoggerContext, get OpenTelemetryAppender from Root Logger, and call setOpenTelemetrySdk(...)
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
((OpenTelemetryAppender)
loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender("OpenTelemetryAppender"))
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
.setOpenTelemetry(openTelemetrySdk);

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

Optionally, you can configure `OpenTelemetryAppender` to use the `GlobalOpenTelemetry` instance:
```xml
<appender name="OpenTelemetry"
class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
<useGlobalOpenTelemetry>true</useGlobalOpenTelemetry>
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
</appender>
```

Note: Setting `useGlobalOpenTelemetry` causes `OpenTelemetryAppender` to
call `GlobalOpenTelemetry.get()`, which initializes `GlobalOpenTelemetry` and prevents future calls
to `GlobalOpenTelemetry.set(...)`. `GlobalOpenTelemetry.get()` will return a noop instance unless
you've opted
into [autoconfigure](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure)
via:

- Add dependency on `io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:OPENTELEMETRY_VERSION`
- Opt into `GlobalOpenTelemetry` autoconfigure by
setting `-Dotel.java.global-autoconfigure.enabled=true`
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ dependencies {
}

testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")

testAnnotationProcessor("com.google.auto.service:auto-service")
testCompileOnly("com.google.auto.service:auto-service-annotations")
}

graalvmNative {
Expand Down Expand Up @@ -83,7 +87,28 @@ testing {
}

tasks {
val testUseGlobalOpenTelemetry by registering(Test::class) {
filter {
includeTestsMatching("*OpenTelemetryAppenderGlobalTest*")
}
include("**/*OpenTelemetryAppenderGlobalTest.*")
jvmArgs(
"-Dotel.java.global-autoconfigure.enabled=true",
"-Dlogback.configurationFile=" + project.projectDir.toString() + "/src/test/resources/logback-use-global.xml",
"-Dotel.metrics.exporter=none",
"-Dotel.traces.exporter=none",
"-Dotel.logs.exporter=none"
)
}

test {
filter {
excludeTestsMatching("*OpenTelemetryAppenderGlobalTest")
}
}

check {
dependsOn(testUseGlobalOpenTelemetry)
dependsOn(testing.suites)
}
}
Loading