From 1ae8e77b0929a6b110474d0fdc4ab8d2a00e1d3d Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Fri, 31 Oct 2025 12:36:55 +0100 Subject: [PATCH 1/2] fix(logging): Fix bug where correlation_id field was missing in JSON structured log. --- .../json/resolver/PowertoolsResolver.java | 18 ++++++++++++++++++ .../src/main/resources/LambdaEcsLayout.json | 14 +++++++++----- .../src/main/resources/LambdaJsonLayout.json | 4 ++++ .../PowertoolsResolverArgumentsTest.java | 6 ++++-- .../logging/logback/LambdaEcsEncoder.java | 7 +++++++ .../logging/internal/LambdaEcsEncoderTest.java | 4 +++- .../internal/LambdaJsonEncoderTest.java | 6 ++++-- 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java index 8ada50f49..3606abd24 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java +++ b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java @@ -15,6 +15,7 @@ package org.apache.logging.log4j.layout.template.json.resolver; import static java.util.Arrays.stream; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; @@ -142,6 +143,22 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { } }; + private static final EventResolver CORRELATION_ID_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String correlationId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.CORRELATION_ID.getName()); + return null != correlationId; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String correlationId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.CORRELATION_ID.getName()); + jsonWriter.writeString(correlationId); + } + }; + private static final EventResolver SERVICE_RESOLVER = (final LogEvent logEvent, final JsonWriter jsonWriter) -> { final String service = logEvent.getContextData().getValue(PowertoolsLoggedFields.SERVICE.getName()); @@ -214,6 +231,7 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { { FUNCTION_REQUEST_ID.getName(), FUNCTION_REQ_RESOLVER }, { FUNCTION_COLD_START.getName(), COLD_START_RESOLVER }, { FUNCTION_TRACE_ID.getName(), XRAY_TRACE_RESOLVER }, + { CORRELATION_ID.getName(), CORRELATION_ID_RESOLVER }, { SAMPLING_RATE.getName(), SAMPLING_RATE_RESOLVER }, { "region", REGION_RESOLVER }, { "account_id", ACCOUNT_ID_RESOLVER } diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json index 19f13f199..58b30f60e 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json @@ -45,13 +45,13 @@ "$resolver": "thread", "field": "name" }, - "cloud.provider" : "aws", - "cloud.service.name" : "lambda", - "cloud.region" : { + "cloud.provider": "aws", + "cloud.service.name": "lambda", + "cloud.region": { "$resolver": "powertools", "field": "region" }, - "cloud.account.id" : { + "cloud.account.id": { "$resolver": "powertools", "field": "account_id" }, @@ -83,7 +83,11 @@ "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation.id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } -} \ No newline at end of file +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json index 8b811ee5f..793006502 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -65,6 +65,10 @@ "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java index 546d54579..573eaddbf 100644 --- a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java @@ -90,7 +90,8 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { .contains( "\"input\":{\"awsRegion\":\"us-east-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); @@ -122,7 +123,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { .contains( "\"input\":{\"awsRegion\":\"us-east-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java index a1a7daff1..6a82d8e67 100644 --- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.logging.logback; import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; @@ -72,6 +73,7 @@ public class LambdaEcsEncoder extends EncoderBase { protected static final String FUNCTION_VERSION_ATTR_NAME = "faas.version"; protected static final String FUNCTION_MEMORY_ATTR_NAME = "faas.memory"; protected static final String FUNCTION_TRACE_ID_ATTR_NAME = "trace.id"; + protected static final String CORRELATION_ID_ATTR_NAME = "correlation.id"; protected static final String ECS_VERSION = "1.2.0"; protected static final String CLOUD_PROVIDER = "aws"; @@ -156,6 +158,11 @@ private void serializeFunctionInfo(JsonSerializer serializer, String arn, Map { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); @@ -168,7 +169,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { "\"input\":{\"awsRegion\":\"eu-central-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") // Should auto-escape double quotes around id - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); From db4b739c8137f86f98c8616abc5f97b00e8edad4 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Fri, 31 Oct 2025 12:41:13 +0100 Subject: [PATCH 2/2] Remove useless parantheses. --- .../log4j/layout/template/json/resolver/PowertoolsResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java index 3606abd24..cef5b86ee 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java +++ b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java @@ -113,7 +113,7 @@ public boolean isResolvable(LogEvent logEvent) { final String samplingRate = logEvent.getContextData().getValue(PowertoolsLoggedFields.SAMPLING_RATE.getName()); try { - return (null != samplingRate && Float.parseFloat(samplingRate) > 0.f); + return null != samplingRate && Float.parseFloat(samplingRate) > 0.f; } catch (NumberFormatException nfe) { return false; }