Skip to content

Commit

Permalink
Capture logback logger context properties
Browse files Browse the repository at this point in the history
  • Loading branch information
laurit committed Sep 25, 2023
1 parent bd85196 commit f5a8739
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
| `otel.instrumentation.logback-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. |
| `otel.instrumentation.logback-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. |
| `otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. |
| `otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. |
| `otel.instrumentation.logback-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |

[source code attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#source-code-attributes
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public final class LogbackSingletons {
config.getBoolean(
"otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes",
false);
boolean captureLoggerContext = config.getBoolean(
"otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes",
false);
List<String> captureMdcAttributes =
config.getList(
"otel.instrumentation.logback-appender.experimental.capture-mdc-attributes",
Expand All @@ -42,7 +45,8 @@ public final class LogbackSingletons {
captureMdcAttributes,
captureCodeAttributes,
captureMarkerAttribute,
captureKeyValuePairAttributes);
captureKeyValuePairAttributes,
captureLoggerContext);
}

public static LoggingEventMapper mapper() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,12 @@ Settings can be configured in `logback.xml`, for example:
The available settings are:

| XML Element | Type | Default | Description |
| ------------------------------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|---------------------------------| ------- | ------- |-----------------------------------------------------------------------------------------------------------------------------------------------|
| `captureExperimentalAttributes` | Boolean | `false` | Enable the capture of experimental span attributes `thread.name` and `thread.id`. |
| `captureCodeAttributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. |
| `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. |
| `captureKeyValuePairAttributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. |
| `captureLoggerContext` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. |
| `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |

[source code attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md#source-code-attributes
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase<ILoggingEv
private boolean captureCodeAttributes = false;
private boolean captureMarkerAttribute = false;
private boolean captureKeyValuePairAttributes = false;
private boolean captureLoggerContext = false;
private List<String> captureMdcAttributes = emptyList();

private OpenTelemetry openTelemetry;
Expand Down Expand Up @@ -62,7 +63,8 @@ public void start() {
captureMdcAttributes,
captureCodeAttributes,
captureMarkerAttribute,
captureKeyValuePairAttributes);
captureKeyValuePairAttributes,
captureLoggerContext);
if (openTelemetry == null) {
openTelemetry = OpenTelemetry.noop();
}
Expand Down Expand Up @@ -98,7 +100,7 @@ public void setCaptureCodeAttributes(boolean captureCodeAttributes) {
/**
* Sets whether the marker attribute should be set to logs.
*
* @param captureMarkerAttribute To enable or disable the marker attribute
* @param captureMarkerAttribute To enable or disable capturing the marker attribute
*/
public void setCaptureMarkerAttribute(boolean captureMarkerAttribute) {
this.captureMarkerAttribute = captureMarkerAttribute;
Expand All @@ -107,12 +109,21 @@ public void setCaptureMarkerAttribute(boolean captureMarkerAttribute) {
/**
* Sets whether the key value pair attributes should be set to logs.
*
* @param captureKeyValuePairAttributes To enable or disable the marker attribute
* @param captureKeyValuePairAttributes To enable or disable capturing key value pairs
*/
public void setCaptureKeyValuePairAttributes(boolean captureKeyValuePairAttributes) {
this.captureKeyValuePairAttributes = captureKeyValuePairAttributes;
}

/**
* Sets whether the logger context properties should be set to logs.
*
* @param captureLoggerContext To enable or disable capturing logger context properties
*/
public void setCaptureLoggerContext(boolean captureLoggerContext) {
this.captureLoggerContext = captureLoggerContext;
}

/** Configures the {@link MDC} attributes that will be copied to logs. */
public void setCaptureMdcAttributes(String attributes) {
if (attributes != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public final class LoggingEventMapper {
private static final boolean supportsKeyValuePairs = supportsKeyValuePairs();
private static final boolean supportsMultipleMarkers = supportsMultipleMarkers();
private static final Cache<String, AttributeKey<String>> mdcAttributeKeys = Cache.bounded(100);
private static final Cache<String, AttributeKey<String>> attributeKeys = Cache.bounded(100);

private static final AttributeKey<List<String>> LOG_MARKER =
AttributeKey.stringArrayKey("logback.marker");
Expand All @@ -46,18 +47,21 @@ public final class LoggingEventMapper {
private final boolean captureCodeAttributes;
private final boolean captureMarkerAttribute;
private final boolean captureKeyValuePairAttributes;
private final boolean captureLoggerContext;

public LoggingEventMapper(
boolean captureExperimentalAttributes,
List<String> captureMdcAttributes,
boolean captureCodeAttributes,
boolean captureMarkerAttribute,
boolean captureKeyValuePairAttributes) {
boolean captureKeyValuePairAttributes,
boolean captureLoggerContext) {
this.captureExperimentalAttributes = captureExperimentalAttributes;
this.captureCodeAttributes = captureCodeAttributes;
this.captureMdcAttributes = captureMdcAttributes;
this.captureMarkerAttribute = captureMarkerAttribute;
this.captureKeyValuePairAttributes = captureKeyValuePairAttributes;
this.captureLoggerContext = captureLoggerContext;
this.captureAllMdcAttributes =
captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*");
}
Expand Down Expand Up @@ -148,6 +152,10 @@ private void mapLoggingEvent(LogRecordBuilder builder, ILoggingEvent loggingEven
captureKeyValuePairAttributes(attributes, loggingEvent);
}

if (captureLoggerContext) {
captureLoggerContext(attributes, loggingEvent.getLoggerContextVO().getPropertyMap());
}

builder.setAllAttributes(attributes.build());

// span context
Expand All @@ -156,7 +164,6 @@ private void mapLoggingEvent(LogRecordBuilder builder, ILoggingEvent loggingEven

// visible for testing
void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcProperties) {

if (captureAllMdcAttributes) {
for (Map.Entry<String, String> entry : mdcProperties.entrySet()) {
attributes.put(getMdcAttributeKey(entry.getKey()), entry.getValue());
Expand Down Expand Up @@ -212,12 +219,23 @@ private static void captureKeyValuePairAttributes(
if (keyValuePairs != null) {
for (KeyValuePair keyValuePair : keyValuePairs) {
if (keyValuePair.value != null) {
attributes.put(keyValuePair.key, keyValuePair.value.toString());
attributes.put(getAttributeKey(keyValuePair.key), keyValuePair.value.toString());
}
}
}
}

private static void captureLoggerContext(
AttributesBuilder attributes, Map<String, String> loggerContextProperties) {
for (Map.Entry<String, String> entry : loggerContextProperties.entrySet()) {
attributes.put(getAttributeKey(entry.getKey()), entry.getValue());
}
}

public static AttributeKey<String> getAttributeKey(String key) {
return attributeKeys.computeIfAbsent(key, k -> AttributeKey.stringKey(k));
}

private static boolean supportsKeyValuePairs() {
try {
Class.forName("org.slf4j.event.KeyValuePair");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.ContextBase;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.Span;
Expand All @@ -21,9 +23,12 @@
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.semconv.SemanticAttributes;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -57,6 +62,30 @@ static void setupAll() {
OpenTelemetrySdk.builder().setLoggerProvider(loggerProvider).build();

OpenTelemetryAppender.install(openTelemetrySdk);

// by default LoggerContext contains HOSTNAME property we clear it to start with empty context
resetLoggerContext();
}

private static void resetLoggerContext() {
try {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Field field = ContextBase.class.getDeclaredField("propertyMap");
field.setAccessible(true);
Map<?, ?> map = (Map<?, ?>) field.get(loggerContext);
map.clear();

Method method;
try {
method = LoggerContext.class.getDeclaredMethod("syncRemoteView");
} catch (NoSuchMethodException noSuchMethodException) {
method = LoggerContext.class.getDeclaredMethod("updateLoggerContextVO");
}
method.setAccessible(true);
method.invoke(loggerContext);
} catch (Exception exception) {
throw new IllegalStateException("Failed to reset logger context", exception);
}
}

@BeforeEach
Expand Down Expand Up @@ -170,4 +199,25 @@ void logContextData() {
assertThat(logData.getAttributes().get(AttributeKey.stringKey("logback.mdc.key2")))
.isEqualTo("val2");
}

@Test
void logLoggerContext() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.putProperty("test-property", "test-value");
try {
logger.info("log message 1");
} finally {
resetLoggerContext();
}

List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems();
assertThat(logDataList).hasSize(1);
LogRecordData logData = logDataList.get(0);
assertThat(logData.getResource()).isEqualTo(resource);
assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo);
assertThat(logData.getBody().asString()).isEqualTo("log message 1");
assertThat(logData.getAttributes().size()).isEqualTo(1 + 4); // 4 code attributes
assertThat(logData.getAttributes().get(AttributeKey.stringKey("test-property")))
.isEqualTo("test-value");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class LoggingEventMapperTest {
@Test
void testDefault() {
// given
LoggingEventMapper mapper = new LoggingEventMapper(false, emptyList(), false, false, false);
LoggingEventMapper mapper =
new LoggingEventMapper(false, emptyList(), false, false, false, false);
Map<String, String> contextData = new HashMap<>();
contextData.put("key1", "value1");
contextData.put("key2", "value2");
Expand All @@ -39,7 +40,7 @@ void testDefault() {
void testSome() {
// given
LoggingEventMapper mapper =
new LoggingEventMapper(false, singletonList("key2"), false, false, false);
new LoggingEventMapper(false, singletonList("key2"), false, false, false, false);
Map<String, String> contextData = new HashMap<>();
contextData.put("key1", "value1");
contextData.put("key2", "value2");
Expand All @@ -57,7 +58,7 @@ void testSome() {
void testAll() {
// given
LoggingEventMapper mapper =
new LoggingEventMapper(false, singletonList("*"), false, false, false);
new LoggingEventMapper(false, singletonList("*"), false, false, false, false);
Map<String, String> contextData = new HashMap<>();
contextData.put("key1", "value1");
contextData.put("key2", "value2");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<captureExperimentalAttributes>false</captureExperimentalAttributes>
<captureCodeAttributes>true</captureCodeAttributes>
<captureMarkerAttribute>true</captureMarkerAttribute>
<captureLoggerContext>true</captureLoggerContext>
<captureMdcAttributes>*</captureMdcAttributes>
</appender>

Expand Down

0 comments on commit f5a8739

Please sign in to comment.