Skip to content

Commit 2b4b6a3

Browse files
committed
feat(logging): add support for key-value pairs in LambdaJsonEncoder
1 parent 61fd80a commit 2b4b6a3

3 files changed

Lines changed: 77 additions & 8 deletions

File tree

powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ static void serializeMDCEntry(Map.Entry<String, String> entry, JsonSerializer se
7373
}
7474
}
7575

76+
static void serializeKVPEntry(String key, Object value, JsonSerializer serializer) {
77+
serializer.writeRaw(',');
78+
serializer.writeFieldName(key);
79+
serializer.writeObject(value);
80+
}
81+
7682
static void serializeArguments(ILoggingEvent event, JsonSerializer serializer) throws IOException {
7783
Object[] arguments = event.getArgumentArray();
7884
if (arguments != null) {

powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@
1515
package software.amazon.lambda.powertools.logging.logback;
1616

1717
import static java.nio.charset.StandardCharsets.UTF_8;
18-
import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeArguments;
19-
import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntries;
20-
import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntry;
21-
import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeTimestamp;
18+
import static software.amazon.lambda.powertools.logging.logback.JsonUtils.*;
2219

2320
import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
2421
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
@@ -27,10 +24,8 @@
2724
import ch.qos.logback.classic.spi.ThrowableProxy;
2825
import ch.qos.logback.core.encoder.EncoderBase;
2926
import java.io.IOException;
30-
import java.util.Arrays;
31-
import java.util.HashMap;
32-
import java.util.Map;
33-
import java.util.TreeMap;
27+
import java.util.*;
28+
3429
import software.amazon.lambda.powertools.logging.internal.JsonSerializer;
3530
import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields;
3631

@@ -88,6 +83,8 @@ public byte[] encode(ILoggingEvent event) {
8883

8984
serializeMDCEntries(sortedMap, serializer);
9085

86+
serializeKeyValuePairs(event, serializer);
87+
9188
serializeArguments(event, serializer);
9289

9390
serializeThreadInfo(event, serializer);
@@ -104,6 +101,13 @@ public byte[] encode(ILoggingEvent event) {
104101
return builder.toString().getBytes(UTF_8);
105102
}
106103

104+
private void serializeKeyValuePairs(ILoggingEvent event, JsonSerializer serializer) {
105+
Optional.ofNullable(event.getKeyValuePairs())
106+
.orElse(Collections.emptyList()).stream()
107+
.filter(Objects::nonNull)
108+
.forEach(kvp -> serializeKVPEntry(String.valueOf(kvp.key), kvp.value, serializer));
109+
}
110+
107111
private void serializeThreadInfo(ILoggingEvent event, JsonSerializer serializer) {
108112
if (includeThreadInfo) {
109113
if (event.getThreadName() != null) {

powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.nio.file.StandardOpenOption;
4141
import java.text.SimpleDateFormat;
4242
import java.util.Arrays;
43+
import java.util.List;
4344
import java.util.Collections;
4445
import java.util.Date;
4546
import java.util.TimeZone;
@@ -50,6 +51,7 @@
5051
import org.junit.jupiter.api.BeforeEach;
5152
import org.junit.jupiter.api.Order;
5253
import org.junit.jupiter.api.Test;
54+
import org.slf4j.event.KeyValuePair;
5355
import software.amazon.lambda.powertools.common.stubs.TestLambdaContext;
5456
import org.slf4j.LoggerFactory;
5557
import org.slf4j.MDC;
@@ -442,4 +444,61 @@ void shouldLogException() {
442444
.contains("\"stack\":\"java.lang.IllegalStateException: Unexpected value\\n");
443445
}
444446

447+
@Test
448+
void shouldLogKeyValuePairs() {
449+
// GIVEN
450+
LambdaJsonEncoder encoder = new LambdaJsonEncoder();
451+
encoder.start();
452+
453+
Object[] arguments = {
454+
"argument_01",
455+
StructuredArguments.entry("structured_argument_01_retain", "retained"),
456+
StructuredArguments.entry("structured_argument_02_overwrite", "to_be_overwritten")
457+
};
458+
LoggingEvent keyValuePairsLoggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Key Value Pairs Test with argument: {}",
459+
null, arguments);
460+
461+
MDC.put("mdc_01_retain", "retained");
462+
MDC.put("mdc_02_overwrite", "to_be_overwritten");
463+
464+
keyValuePairsLoggingEvent.setKeyValuePairs(List.of(
465+
new KeyValuePair("key_01_string", "value_01"),
466+
new KeyValuePair("key_02_numeric", 2),
467+
new KeyValuePair("key_03_decimal", 2.333),
468+
new KeyValuePair("key_04_null", null),
469+
new KeyValuePair("", "value_05_empty_key"),
470+
new KeyValuePair(null, "value_06_null_key"),
471+
new KeyValuePair("key_07_boolean_true", true),
472+
new KeyValuePair("key_08_boolean_false", false),
473+
new KeyValuePair("mdc_02_overwrite", "overwritten_by_kvp"),
474+
new KeyValuePair("structured_argument_02_overwrite", "overwritten_by_kvp")
475+
));
476+
477+
// WHEN
478+
byte[] encoded = encoder.encode(keyValuePairsLoggingEvent);
479+
String result = new String(encoded, StandardCharsets.UTF_8);
480+
481+
// THEN
482+
assertThat(result)
483+
// Arguments
484+
.contains("Key Value Pairs Test with argument: argument_01")
485+
.contains("\"structured_argument_01_retain\":\"retained\"")
486+
// .doesNotContain("\"structured_argument_02_overwrite\":\"to_be_overwritten\"") TODO: Deduplication not implemented vor Arguments
487+
// MDC
488+
.contains("\"mdc_01_retain\":\"retained\"")
489+
// .doesNotContain("\"mdc_02_overwrite\":\"to_be_overwritten\"") TODO: Deduplication not implemented vor Arguments
490+
// Key Value Pairs
491+
.contains("\"key_01_string\":\"value_01\"")
492+
.contains("\"key_02_numeric\":2")
493+
.contains("\"key_03_decimal\":2.333")
494+
.contains("\"key_04_null\":null")
495+
.contains("\"\":\"value_05_empty_key\"")
496+
.contains("\"null\":\"value_06_null_key\"")
497+
.contains("\"key_07_boolean_true\":true")
498+
.contains("\"key_08_boolean_false\":false")
499+
.contains("\"mdc_02_overwrite\":\"overwritten_by_kvp\"")
500+
.contains("\"structured_argument_02_overwrite\":\"overwritten_by_kvp\"")
501+
;
502+
}
503+
445504
}

0 commit comments

Comments
 (0)