diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/pom.xml b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/pom.xml
new file mode 100644
index 000000000..8432aa604
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/pom.xml
@@ -0,0 +1,82 @@
+
+ 4.0.0
+
+
+ software.amazon.lambda
+ e2e-test-handlers-parent
+ 2.9.0
+
+
+ e2e-test-handler-logging-log4j-fluent-api
+ jar
+ E2E test handler – Logging Log4j Fluent API
+
+
+
+ software.amazon.lambda
+ powertools-logging-log4j
+
+
+ org.aspectj
+ aspectjrt
+
+
+ com.amazonaws
+ aws-lambda-java-events
+
+
+ com.amazonaws
+ aws-lambda-java-runtime-interface-client
+
+
+ com.amazonaws
+ aws-lambda-java-core
+
+
+
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ ${maven.compiler.target}
+
+
+ software.amazon.lambda
+ powertools-logging
+
+
+
+
+
+
+ compile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+
+
+
+
+ native-image
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+
+
+
+
+
+
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Function.java
new file mode 100644
index 000000000..fbd6d7286
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Function.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 Amazon.com, Inc. or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package software.amazon.lambda.powertools.e2e;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.spi.LoggingEventBuilder;
+import software.amazon.lambda.powertools.logging.Logging;
+import software.amazon.lambda.powertools.logging.PowertoolsLogging;
+
+public class Function implements RequestHandler {
+ private static final Logger LOG = LoggerFactory.getLogger(Function.class);
+
+ @Logging
+ public String handleRequest(Input input, Context context) {
+
+ LoggingEventBuilder loggingEventBuilder = LOG.atInfo().setMessage(input.getMessage());
+ //noinspection ResultOfMethodCallIgnored
+ input.getKeys().forEach(loggingEventBuilder::addKeyValue);
+ loggingEventBuilder.log();
+
+ // Flush buffer manually since we buffer at INFO level to test log buffering
+ PowertoolsLogging.flushBuffer();
+
+ return "OK";
+ }
+}
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Input.java
new file mode 100644
index 000000000..66fd49ddc
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Input.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 Amazon.com, Inc. or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package software.amazon.lambda.powertools.e2e;
+
+import java.util.Map;
+
+public class Input {
+ private String message;
+ private Map keys;
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Map getKeys() {
+ return keys;
+ }
+
+ public void setKeys(Map keys) {
+ this.keys = keys;
+ }
+}
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json
new file mode 100644
index 000000000..2780aca09
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json
@@ -0,0 +1,13 @@
+[
+ {
+ "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime",
+ "methods":[{"name":"","parameterTypes":[] }],
+ "fields":[{"name":"logger"}],
+ "allPublicMethods":true
+ },
+ {
+ "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal",
+ "methods":[{"name":"","parameterTypes":[] }],
+ "allPublicMethods":true
+ }
+]
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json
new file mode 100644
index 000000000..ddda5d5f1
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json
@@ -0,0 +1,35 @@
+[
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ }
+]
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json
new file mode 100644
index 000000000..91be72f7a
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException",
+ "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }]
+ },
+ {
+ "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest",
+ "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}],
+ "allPublicMethods":true
+ }
+]
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties
new file mode 100644
index 000000000..20f8b7801
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties
@@ -0,0 +1 @@
+Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json
new file mode 100644
index 000000000..467af67a0
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json
@@ -0,0 +1,62 @@
+[
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]"
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime",
+ "fields": [{ "name": "logger" }]
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true
+ },
+ {
+ "name": "java.lang.Void",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "java.util.Collections$UnmodifiableMap",
+ "fields": [{ "name": "m" }]
+ },
+ {
+ "name": "jdk.internal.module.IllegalAccessLogger",
+ "fields": [{ "name": "logger" }]
+ },
+ {
+ "name": "sun.misc.Unsafe",
+ "fields": [{ "name": "theUnsafe" }]
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest",
+ "fields": [
+ { "name": "id" },
+ { "name": "invokedFunctionArn" },
+ { "name": "deadlineTimeInMs" },
+ { "name": "xrayTraceId" },
+ { "name": "clientContext" },
+ { "name": "cognitoIdentity" },
+ { "name": "tenantId" },
+ { "name": "content" }
+ ],
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ }
+]
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json
new file mode 100644
index 000000000..1062b4249
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json
@@ -0,0 +1,19 @@
+{
+ "resources": {
+ "includes": [
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E"
+ },
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E"
+ },
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E"
+ },
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E"
+ }
+ ]
+ },
+ "bundles": []
+}
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json
new file mode 100644
index 000000000..9890688f9
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json
@@ -0,0 +1,25 @@
+[
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]"
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]"
+ },
+ {
+ "name": "org.joda.time.DateTime",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ }
+]
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json
new file mode 100644
index 000000000..9ddd235e2
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json
@@ -0,0 +1,20 @@
+[
+ {
+ "name": "software.amazon.lambda.powertools.e2e.Function",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ },
+ {
+ "name": "software.amazon.lambda.powertools.e2e.Input",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ }
+]
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json
new file mode 100644
index 000000000..be6aac3f6
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json
@@ -0,0 +1,7 @@
+{
+ "resources":{
+ "includes":[{
+ "pattern":"\\Qlog4j2.xml\\E"
+ }]},
+ "bundles":[]
+}
diff --git a/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..28e03a9e0
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-log4j-fluent-api/src/main/resources/log4j2.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/pom.xml b/powertools-e2e-tests/handlers/logging-logback-fluent-api/pom.xml
new file mode 100644
index 000000000..83d0abddb
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/pom.xml
@@ -0,0 +1,82 @@
+
+ 4.0.0
+
+
+ software.amazon.lambda
+ e2e-test-handlers-parent
+ 2.9.0
+
+
+ e2e-test-handler-logging-logback-fluent-api
+ jar
+ E2E test handler – Logging Logback Fluent API
+
+
+
+ software.amazon.lambda
+ powertools-logging-logback
+
+
+ org.aspectj
+ aspectjrt
+
+
+ com.amazonaws
+ aws-lambda-java-events
+
+
+ com.amazonaws
+ aws-lambda-java-runtime-interface-client
+
+
+ com.amazonaws
+ aws-lambda-java-core
+
+
+
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ ${maven.compiler.target}
+
+
+ software.amazon.lambda
+ powertools-logging
+
+
+
+
+
+
+ compile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+
+
+
+
+ native-image
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+
+
+
+
+
+
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Function.java
new file mode 100644
index 000000000..fbd6d7286
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Function.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 Amazon.com, Inc. or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package software.amazon.lambda.powertools.e2e;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.spi.LoggingEventBuilder;
+import software.amazon.lambda.powertools.logging.Logging;
+import software.amazon.lambda.powertools.logging.PowertoolsLogging;
+
+public class Function implements RequestHandler {
+ private static final Logger LOG = LoggerFactory.getLogger(Function.class);
+
+ @Logging
+ public String handleRequest(Input input, Context context) {
+
+ LoggingEventBuilder loggingEventBuilder = LOG.atInfo().setMessage(input.getMessage());
+ //noinspection ResultOfMethodCallIgnored
+ input.getKeys().forEach(loggingEventBuilder::addKeyValue);
+ loggingEventBuilder.log();
+
+ // Flush buffer manually since we buffer at INFO level to test log buffering
+ PowertoolsLogging.flushBuffer();
+
+ return "OK";
+ }
+}
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Input.java
new file mode 100644
index 000000000..66fd49ddc
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/java/software/amazon/lambda/powertools/e2e/Input.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 Amazon.com, Inc. or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package software.amazon.lambda.powertools.e2e;
+
+import java.util.Map;
+
+public class Input {
+ private String message;
+ private Map keys;
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Map getKeys() {
+ return keys;
+ }
+
+ public void setKeys(Map keys) {
+ this.keys = keys;
+ }
+}
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json
new file mode 100644
index 000000000..2780aca09
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json
@@ -0,0 +1,13 @@
+[
+ {
+ "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime",
+ "methods":[{"name":"","parameterTypes":[] }],
+ "fields":[{"name":"logger"}],
+ "allPublicMethods":true
+ },
+ {
+ "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal",
+ "methods":[{"name":"","parameterTypes":[] }],
+ "allPublicMethods":true
+ }
+]
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json
new file mode 100644
index 000000000..ddda5d5f1
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json
@@ -0,0 +1,35 @@
+[
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent",
+ "allDeclaredFields": true,
+ "allDeclaredMethods": true,
+ "allDeclaredConstructors": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ }
+]
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json
new file mode 100644
index 000000000..91be72f7a
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException",
+ "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }]
+ },
+ {
+ "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest",
+ "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}],
+ "allPublicMethods":true
+ }
+]
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties
new file mode 100644
index 000000000..20f8b7801
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties
@@ -0,0 +1 @@
+Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json
new file mode 100644
index 000000000..467af67a0
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json
@@ -0,0 +1,62 @@
+[
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]"
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime",
+ "fields": [{ "name": "logger" }]
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true
+ },
+ {
+ "name": "java.lang.Void",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "java.util.Collections$UnmodifiableMap",
+ "fields": [{ "name": "m" }]
+ },
+ {
+ "name": "jdk.internal.module.IllegalAccessLogger",
+ "fields": [{ "name": "logger" }]
+ },
+ {
+ "name": "sun.misc.Unsafe",
+ "fields": [{ "name": "theUnsafe" }]
+ },
+ {
+ "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest",
+ "fields": [
+ { "name": "id" },
+ { "name": "invokedFunctionArn" },
+ { "name": "deadlineTimeInMs" },
+ { "name": "xrayTraceId" },
+ { "name": "clientContext" },
+ { "name": "cognitoIdentity" },
+ { "name": "tenantId" },
+ { "name": "content" }
+ ],
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ }
+]
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json
new file mode 100644
index 000000000..1062b4249
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json
@@ -0,0 +1,19 @@
+{
+ "resources": {
+ "includes": [
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E"
+ },
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E"
+ },
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E"
+ },
+ {
+ "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E"
+ }
+ ]
+ },
+ "bundles": []
+}
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json
new file mode 100644
index 000000000..9890688f9
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json
@@ -0,0 +1,25 @@
+[
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]"
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl",
+ "methods": [{ "name": "", "parameterTypes": [] }]
+ },
+ {
+ "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]"
+ },
+ {
+ "name": "org.joda.time.DateTime",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ }
+]
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json
new file mode 100644
index 000000000..9ddd235e2
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json
@@ -0,0 +1,20 @@
+[
+ {
+ "name": "software.amazon.lambda.powertools.e2e.Function",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ },
+ {
+ "name": "software.amazon.lambda.powertools.e2e.Input",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredClasses": true,
+ "allPublicClasses": true
+ }
+]
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json
new file mode 100644
index 000000000..a603a9398
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json
@@ -0,0 +1,7 @@
+{
+ "resources":{
+ "includes":[{
+ "pattern":"\\Qlogback.xml\\E"
+ }]},
+ "bundles":[]
+}
diff --git a/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/logback.xml b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/logback.xml
new file mode 100644
index 000000000..0a5e4d146
--- /dev/null
+++ b/powertools-e2e-tests/handlers/logging-logback-fluent-api/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+ INFO
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml
index 585bb83db..c9a4826a2 100644
--- a/powertools-e2e-tests/handlers/pom.xml
+++ b/powertools-e2e-tests/handlers/pom.xml
@@ -30,7 +30,9 @@
largemessage-functional
largemessage_idempotent
logging-log4j
+ logging-log4j-fluent-api
logging-logback
+ logging-logback-fluent-api
logging-functional
tracing
metrics
diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java
index 20bc5394d..364029214 100644
--- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java
+++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java
@@ -70,7 +70,7 @@ void tearDown() {
}
@ParameterizedTest
- @ValueSource(strings = { "logging-log4j", "logging-logback", "logging-functional" })
+ @ValueSource(strings = { "logging-log4j", "logging-log4j-fluent-api", "logging-logback", "logging-logback-fluent-api", "logging-functional" })
@Timeout(value = 15, unit = TimeUnit.MINUTES)
void test_logInfoWithAdditionalKeys(String pathToFunction) throws JsonProcessingException {
setupInfrastructure(pathToFunction);
diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java
index 67d6b268d..fbdec8e4a 100644
--- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java
+++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/JsonUtils.java
@@ -73,6 +73,12 @@ static void serializeMDCEntry(Map.Entry entry, JsonSerializer se
}
}
+ static void serializeKVPEntry(String key, Object value, JsonSerializer serializer) {
+ serializer.writeRaw(',');
+ serializer.writeFieldName(key);
+ serializer.writeObject(value);
+ }
+
static void serializeArguments(ILoggingEvent event, JsonSerializer serializer) throws IOException {
Object[] arguments = event.getArgumentArray();
if (arguments != null) {
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 6a82d8e67..d9d90b09b 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
@@ -23,9 +23,7 @@
import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_REQUEST_ID;
import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_TRACE_ID;
import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_VERSION;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeArguments;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntries;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeTimestamp;
+import static software.amazon.lambda.powertools.logging.logback.JsonUtils.*;
import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
@@ -34,8 +32,8 @@
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.encoder.EncoderBase;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Map;
+import java.util.*;
+
import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor;
import software.amazon.lambda.powertools.logging.internal.JsonSerializer;
@@ -132,6 +130,8 @@ public byte[] encode(ILoggingEvent event) {
serializeMDCEntries(mdcPropertyMap, serializer);
+ serializeKeyValuePairs(event, serializer);
+
serializeArguments(event, serializer);
serializer.writeEndObject();
@@ -142,6 +142,13 @@ public byte[] encode(ILoggingEvent event) {
return builder.toString().getBytes(UTF_8);
}
+ private void serializeKeyValuePairs(ILoggingEvent event, JsonSerializer serializer) {
+ Optional.ofNullable(event.getKeyValuePairs())
+ .orElse(Collections.emptyList()).stream()
+ .filter(Objects::nonNull)
+ .forEach(kvp -> serializeKVPEntry(String.valueOf(kvp.key), kvp.value, serializer));
+ }
+
private void serializeFunctionInfo(JsonSerializer serializer, String arn, Map mdcPropertyMap) {
if (includeFaasInfo) {
serializer.writeRaw(',');
diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java
index 9afaf0ab7..894c3081b 100644
--- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java
+++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaJsonEncoder.java
@@ -15,10 +15,7 @@
package software.amazon.lambda.powertools.logging.logback;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeArguments;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntries;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeMDCEntry;
-import static software.amazon.lambda.powertools.logging.logback.JsonUtils.serializeTimestamp;
+import static software.amazon.lambda.powertools.logging.logback.JsonUtils.*;
import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
@@ -27,10 +24,8 @@
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.encoder.EncoderBase;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
+import java.util.*;
+
import software.amazon.lambda.powertools.logging.internal.JsonSerializer;
import software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields;
@@ -88,6 +83,8 @@ public byte[] encode(ILoggingEvent event) {
serializeMDCEntries(sortedMap, serializer);
+ serializeKeyValuePairs(event, serializer);
+
serializeArguments(event, serializer);
serializeThreadInfo(event, serializer);
@@ -104,6 +101,13 @@ public byte[] encode(ILoggingEvent event) {
return builder.toString().getBytes(UTF_8);
}
+ private void serializeKeyValuePairs(ILoggingEvent event, JsonSerializer serializer) {
+ Optional.ofNullable(event.getKeyValuePairs())
+ .orElse(Collections.emptyList()).stream()
+ .filter(Objects::nonNull)
+ .forEach(kvp -> serializeKVPEntry(String.valueOf(kvp.key), kvp.value, serializer));
+ }
+
private void serializeThreadInfo(ILoggingEvent event, JsonSerializer serializer) {
if (includeThreadInfo) {
if (event.getThreadName() != null) {
diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java
index 30ede8ba8..ff7c086ff 100644
--- a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java
+++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaEcsEncoderTest.java
@@ -25,6 +25,7 @@
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.AfterEach;
@@ -40,11 +41,14 @@
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter;
import ch.qos.logback.classic.spi.LoggingEvent;
+import org.slf4j.event.KeyValuePair;
import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor;
import software.amazon.lambda.powertools.common.stubs.TestLambdaContext;
import software.amazon.lambda.powertools.logging.PowertoolsLogging;
+import software.amazon.lambda.powertools.logging.argument.StructuredArguments;
import software.amazon.lambda.powertools.logging.internal.handler.PowertoolsLogEnabled;
import software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder;
+import software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder;
@Order(3)
class LambdaEcsEncoderTest {
@@ -179,4 +183,61 @@ private void setMDC() {
MDC.put(PowertoolsLoggedFields.CORRELATION_ID.getName(), "test-correlation-id");
}
+ @Test
+ void shouldLogKeyValuePairs() {
+ // GIVEN
+ LambdaEcsEncoder encoder = new LambdaEcsEncoder();
+ encoder.start();
+
+ Object[] arguments = {
+ "argument_01",
+ StructuredArguments.entry("structured_argument_01_retain", "retained"),
+ StructuredArguments.entry("structured_argument_02_overwrite", "to_be_overwritten")
+ };
+ LoggingEvent keyValuePairsLoggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Key Value Pairs Test with argument: {}",
+ null, arguments);
+
+ MDC.put("mdc_01_retain", "retained");
+ MDC.put("mdc_02_overwrite", "to_be_overwritten");
+
+ keyValuePairsLoggingEvent.setKeyValuePairs(List.of(
+ new KeyValuePair("key_01_string", "value_01"),
+ new KeyValuePair("key_02_numeric", 2),
+ new KeyValuePair("key_03_decimal", 2.333),
+ new KeyValuePair("key_04_null", null),
+ new KeyValuePair("", "value_05_empty_key"),
+ new KeyValuePair(null, "value_06_null_key"),
+ new KeyValuePair("key_07_boolean_true", true),
+ new KeyValuePair("key_08_boolean_false", false),
+ new KeyValuePair("mdc_02_overwrite", "overwritten_by_kvp"),
+ new KeyValuePair("structured_argument_02_overwrite", "overwritten_by_kvp")
+ ));
+
+ // WHEN
+ byte[] encoded = encoder.encode(keyValuePairsLoggingEvent);
+ String result = new String(encoded, StandardCharsets.UTF_8);
+
+ // THEN
+ assertThat(result)
+ // Arguments
+ .contains("Key Value Pairs Test with argument: argument_01")
+ .contains("\"structured_argument_01_retain\":\"retained\"")
+ // .doesNotContain("\"structured_argument_02_overwrite\":\"to_be_overwritten\"") TODO: Deduplication not implemented vor Arguments
+ // MDC
+ .contains("\"mdc_01_retain\":\"retained\"")
+ // .doesNotContain("\"mdc_02_overwrite\":\"to_be_overwritten\"") TODO: Deduplication not implemented vor Arguments
+ // Key Value Pairs
+ .contains("\"key_01_string\":\"value_01\"")
+ .contains("\"key_02_numeric\":2")
+ .contains("\"key_03_decimal\":2.333")
+ .contains("\"key_04_null\":null")
+ .contains("\"\":\"value_05_empty_key\"")
+ .contains("\"null\":\"value_06_null_key\"")
+ .contains("\"key_07_boolean_true\":true")
+ .contains("\"key_08_boolean_false\":false")
+ .contains("\"mdc_02_overwrite\":\"overwritten_by_kvp\"")
+ .contains("\"structured_argument_02_overwrite\":\"overwritten_by_kvp\"")
+ ;
+ }
+
}
diff --git a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java
index 16bd9e92a..7e77b3e89 100644
--- a/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java
+++ b/powertools-logging/powertools-logging-logback/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonEncoderTest.java
@@ -40,6 +40,7 @@
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.List;
import java.util.Collections;
import java.util.Date;
import java.util.TimeZone;
@@ -50,6 +51,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
+import org.slf4j.event.KeyValuePair;
import software.amazon.lambda.powertools.common.stubs.TestLambdaContext;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
@@ -442,4 +444,61 @@ void shouldLogException() {
.contains("\"stack\":\"java.lang.IllegalStateException: Unexpected value\\n");
}
+ @Test
+ void shouldLogKeyValuePairs() {
+ // GIVEN
+ LambdaJsonEncoder encoder = new LambdaJsonEncoder();
+ encoder.start();
+
+ Object[] arguments = {
+ "argument_01",
+ StructuredArguments.entry("structured_argument_01_retain", "retained"),
+ StructuredArguments.entry("structured_argument_02_overwrite", "to_be_overwritten")
+ };
+ LoggingEvent keyValuePairsLoggingEvent = new LoggingEvent("fqcn", logger, Level.INFO, "Key Value Pairs Test with argument: {}",
+ null, arguments);
+
+ MDC.put("mdc_01_retain", "retained");
+ MDC.put("mdc_02_overwrite", "to_be_overwritten");
+
+ keyValuePairsLoggingEvent.setKeyValuePairs(List.of(
+ new KeyValuePair("key_01_string", "value_01"),
+ new KeyValuePair("key_02_numeric", 2),
+ new KeyValuePair("key_03_decimal", 2.333),
+ new KeyValuePair("key_04_null", null),
+ new KeyValuePair("", "value_05_empty_key"),
+ new KeyValuePair(null, "value_06_null_key"),
+ new KeyValuePair("key_07_boolean_true", true),
+ new KeyValuePair("key_08_boolean_false", false),
+ new KeyValuePair("mdc_02_overwrite", "overwritten_by_kvp"),
+ new KeyValuePair("structured_argument_02_overwrite", "overwritten_by_kvp")
+ ));
+
+ // WHEN
+ byte[] encoded = encoder.encode(keyValuePairsLoggingEvent);
+ String result = new String(encoded, StandardCharsets.UTF_8);
+
+ // THEN
+ assertThat(result)
+ // Arguments
+ .contains("Key Value Pairs Test with argument: argument_01")
+ .contains("\"structured_argument_01_retain\":\"retained\"")
+ // .doesNotContain("\"structured_argument_02_overwrite\":\"to_be_overwritten\"") TODO: Deduplication not implemented vor Arguments
+ // MDC
+ .contains("\"mdc_01_retain\":\"retained\"")
+ // .doesNotContain("\"mdc_02_overwrite\":\"to_be_overwritten\"") TODO: Deduplication not implemented vor Arguments
+ // Key Value Pairs
+ .contains("\"key_01_string\":\"value_01\"")
+ .contains("\"key_02_numeric\":2")
+ .contains("\"key_03_decimal\":2.333")
+ .contains("\"key_04_null\":null")
+ .contains("\"\":\"value_05_empty_key\"")
+ .contains("\"null\":\"value_06_null_key\"")
+ .contains("\"key_07_boolean_true\":true")
+ .contains("\"key_08_boolean_false\":false")
+ .contains("\"mdc_02_overwrite\":\"overwritten_by_kvp\"")
+ .contains("\"structured_argument_02_overwrite\":\"overwritten_by_kvp\"")
+ ;
+ }
+
}