Skip to content

Commit

Permalink
Save ILoggingEvent.getArgumentArray() arguments from Logback (#11865)
Browse files Browse the repository at this point in the history
Co-authored-by: Lauri Tulmin <[email protected]>
  • Loading branch information
igor-suhorukov and laurit authored Aug 15, 2024
1 parent dee515d commit 023f30c
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
| `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-arguments` | Boolean | `false` | Enable the capture of Logback logger arguments. |
| `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/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public final class LogbackSingletons {
config.getBoolean(
"otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes",
false);
boolean captureArguments =
config.getBoolean(
"otel.instrumentation.logback-appender.experimental.capture-arguments", false);
List<String> captureMdcAttributes =
config.getList(
"otel.instrumentation.logback-appender.experimental.capture-mdc-attributes",
Expand All @@ -49,6 +52,7 @@ public final class LogbackSingletons {
.setCaptureMarkerAttribute(captureMarkerAttribute)
.setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes)
.setCaptureLoggerContext(captureLoggerContext)
.setCaptureArguments(captureArguments)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ The available settings are:
| `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. |
| `captureArguments` | Boolean | `false` | Enable the capture of Logback logger arguments. |
| `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. thread.id attribute is not captured. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase<ILoggingEv
private boolean captureMarkerAttribute = false;
private boolean captureKeyValuePairAttributes = false;
private boolean captureLoggerContext = false;
private boolean captureArguments = true;
private List<String> captureMdcAttributes = emptyList();

private volatile OpenTelemetry openTelemetry;
Expand Down Expand Up @@ -79,6 +80,7 @@ public void start() {
.setCaptureMarkerAttribute(captureMarkerAttribute)
.setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes)
.setCaptureLoggerContext(captureLoggerContext)
.setCaptureArguments(captureArguments)
.build();
eventsToReplay = new ArrayBlockingQueue<>(numLogsCapturedBeforeOtelInstall);
super.start();
Expand Down Expand Up @@ -164,6 +166,15 @@ public void setCaptureLoggerContext(boolean captureLoggerContext) {
this.captureLoggerContext = captureLoggerContext;
}

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

/** 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 @@ -24,9 +24,11 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.slf4j.Marker;
import org.slf4j.event.KeyValuePair;

Expand All @@ -53,6 +55,10 @@ public final class LoggingEventMapper {

private static final AttributeKey<List<String>> LOG_MARKER =
AttributeKey.stringArrayKey("logback.marker");
private static final AttributeKey<String> LOG_BODY_TEMPLATE =
AttributeKey.stringKey("log.body.template");
private static final AttributeKey<List<String>> LOG_BODY_PARAMETERS =
AttributeKey.stringArrayKey("log.body.parameters");

private final boolean captureExperimentalAttributes;
private final List<String> captureMdcAttributes;
Expand All @@ -61,6 +67,7 @@ public final class LoggingEventMapper {
private final boolean captureMarkerAttribute;
private final boolean captureKeyValuePairAttributes;
private final boolean captureLoggerContext;
private final boolean captureArguments;

private LoggingEventMapper(Builder builder) {
this.captureExperimentalAttributes = builder.captureExperimentalAttributes;
Expand All @@ -69,6 +76,7 @@ private LoggingEventMapper(Builder builder) {
this.captureMarkerAttribute = builder.captureMarkerAttribute;
this.captureKeyValuePairAttributes = builder.captureKeyValuePairAttributes;
this.captureLoggerContext = builder.captureLoggerContext;
this.captureArguments = builder.captureArguments;
this.captureAllMdcAttributes =
builder.captureMdcAttributes.size() == 1 && builder.captureMdcAttributes.get(0).equals("*");
}
Expand Down Expand Up @@ -173,6 +181,12 @@ private void mapLoggingEvent(
captureLoggerContext(attributes, loggingEvent.getLoggerContextVO().getPropertyMap());
}

if (captureArguments
&& loggingEvent.getArgumentArray() != null
&& loggingEvent.getArgumentArray().length > 0) {
captureArguments(attributes, loggingEvent.getMessage(), loggingEvent.getArgumentArray());
}

builder.setAllAttributes(attributes.build());

// span context
Expand Down Expand Up @@ -218,6 +232,13 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP
}
}

void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) {
attributes.put(LOG_BODY_TEMPLATE, message);
attributes.put(
LOG_BODY_PARAMETERS,
Arrays.stream(arguments).map(String::valueOf).collect(Collectors.toList()));
}

public static AttributeKey<String> getMdcAttributeKey(String key) {
return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey);
}
Expand Down Expand Up @@ -258,19 +279,20 @@ private static void captureKeyValuePairAttributes(
if (keyValuePairs != null) {
for (KeyValuePair keyValuePair : keyValuePairs) {
Object value = keyValuePair.value;
if (keyValuePair.value != null) {
if (value != null) {
String key = keyValuePair.key;
// preserve type for boolean and numeric values, everything else is converted to String
if (value instanceof Boolean) {
attributes.put(keyValuePair.key, (Boolean) keyValuePair.value);
attributes.put(key, (Boolean) value);
} else if (value instanceof Byte
|| value instanceof Integer
|| value instanceof Long
|| value instanceof Short) {
attributes.put(keyValuePair.key, ((Number) keyValuePair.value).longValue());
attributes.put(key, ((Number) value).longValue());
} else if (value instanceof Double || value instanceof Float) {
attributes.put(keyValuePair.key, ((Number) keyValuePair.value).doubleValue());
attributes.put(key, ((Number) value).doubleValue());
} else {
attributes.put(getAttributeKey(keyValuePair.key), keyValuePair.value.toString());
attributes.put(getAttributeKey(key), value.toString());
}
}
}
Expand Down Expand Up @@ -358,6 +380,7 @@ public static final class Builder {
private boolean captureMarkerAttribute;
private boolean captureKeyValuePairAttributes;
private boolean captureLoggerContext;
private boolean captureArguments;

Builder() {}

Expand Down Expand Up @@ -397,6 +420,12 @@ public Builder setCaptureLoggerContext(boolean captureLoggerContext) {
return this;
}

@CanIgnoreReturnValue
public Builder setCaptureArguments(boolean captureArguments) {
this.captureArguments = captureArguments;
return this;
}

public LoggingEventMapper build() {
return new LoggingEventMapper(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,43 @@ void multipleMarkers() {
AttributeKey.stringArrayKey("logback.marker"),
value -> assertThat(value).isEqualTo(Arrays.asList(markerName1, markerName2)));
}

@Test
void arguments() {
logger
.atInfo()
.setMessage("log message {} and {}, bool {}, long {}")
.addArgument("'world'")
.addArgument(Math.PI)
.addArgument(true)
.addArgument(Long.MAX_VALUE)
.log();

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 'world' and 3.141592653589793, bool true, long 9223372036854775807");
assertThat(logData.getAttributes().size()).isEqualTo(6);
assertThat(logData.getAttributes())
.hasEntrySatisfying(
AttributeKey.stringArrayKey("log.body.parameters"),
value ->
assertThat(value)
.isEqualTo(
Arrays.asList(
"'world'",
String.valueOf(Math.PI),
String.valueOf(true),
String.valueOf(Long.MAX_VALUE))));
assertThat(logData)
.hasAttributesSatisfying(
equalTo(
AttributeKey.stringKey("log.body.template"),
"log message {} and {}, bool {}, long {}"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<captureCodeAttributes>true</captureCodeAttributes>
<captureMarkerAttribute>true</captureMarkerAttribute>
<captureKeyValuePairAttributes>true</captureKeyValuePairAttributes>
<captureArguments>true</captureArguments>
<captureMdcAttributes>*</captureMdcAttributes>
</appender>

Expand Down

0 comments on commit 023f30c

Please sign in to comment.