From 96e73dd22aee69d4e4828f636b63477d01384ed5 Mon Sep 17 00:00:00 2001 From: Jeevan Yewale Date: Wed, 21 Jan 2026 12:17:55 +0530 Subject: [PATCH 1/2] Add TenantId to Logger default properties Signed-off-by: Jeevan Yewale --- .../json/resolver/PowertoolsResolver.java | 18 ++++++++++++++++ .../src/main/resources/LambdaEcsLayout.json | 4 ++++ .../src/main/resources/LambdaJsonLayout.json | 4 ++++ .../logging/logback/LambdaEcsEncoder.java | 7 +++++++ .../logging/logback/LambdaJsonEncoder.java | 1 + .../internal/PowertoolsLoggedFields.java | 21 ++++++++++++++++++- 6 files changed, 54 insertions(+), 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 cef5b86ee..8d7252093 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 @@ -25,6 +25,7 @@ import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_VERSION; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SAMPLING_RATE; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.SERVICE; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.TENANT_ID; import java.io.IOException; import java.util.Collections; @@ -168,6 +169,22 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { private static final EventResolver REGION_RESOLVER = (final LogEvent logEvent, final JsonWriter jsonWriter) -> jsonWriter.writeString(SystemWrapper.getenv(LambdaConstants.AWS_REGION_ENV)); + + private static final EventResolver TENANT_ID_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String tenantId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.TENANT_ID.getName()); + return null != tenantId; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String tenantId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.TENANT_ID.getName()); + jsonWriter.writeString(tenantId); + } + }; public static final String LAMBDA_ARN_REGEX = "^arn:(aws|aws-us-gov|aws-cn):lambda:[a-zA-Z0-9-]+:\\d{12}:function:[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$"; @@ -233,6 +250,7 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { { FUNCTION_TRACE_ID.getName(), XRAY_TRACE_RESOLVER }, { CORRELATION_ID.getName(), CORRELATION_ID_RESOLVER }, { SAMPLING_RATE.getName(), SAMPLING_RATE_RESOLVER }, + { TENANT_ID.getName(), TENANT_ID_RESOLVER }, { "region", REGION_RESOLVER }, { "account_id", ACCOUNT_ID_RESOLVER } }).collect(Collectors.toMap(data -> (String) data[0], data -> (EventResolver) data[1]))); 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 58b30f60e..a54d8f8b6 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json @@ -87,6 +87,10 @@ "$resolver": "powertools", "field": "correlation_id" }, + "tenant.id": { + "$resolver": "powertools", + "field": "tenant_id" + }, "": { "$resolver": "powertools" } 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 793006502..e9991638c 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -69,6 +69,10 @@ "$resolver": "powertools", "field": "correlation_id" }, + "tenant_id": { + "$resolver": "powertools", + "field": "tenant_id" + }, "": { "$resolver": "powertools" } 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..d4c78340e 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,6 +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.internal.PowertoolsLoggedFields.TENANT_ID; 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; @@ -74,6 +75,7 @@ public class LambdaEcsEncoder extends EncoderBase { 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 TENANT_ID_ATTR_NAME = "tenant.id"; protected static final String ECS_VERSION = "1.2.0"; protected static final String CLOUD_PROVIDER = "aws"; @@ -163,6 +165,11 @@ private void serializeFunctionInfo(JsonSerializer serializer, String arn, Mapxray_trace_id *
  • sampling_rate
  • *
  • service
  • + *
  • tenant_id
  • * *
    * We strongly recommend to keep these information. diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java index 2545396d2..7b08021d8 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.logging.internal; import com.amazonaws.services.lambda.runtime.Context; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,7 +36,8 @@ public enum PowertoolsLoggedFields { FUNCTION_TRACE_ID("xray_trace_id"), SAMPLING_RATE("sampling_rate"), CORRELATION_ID("correlation_id"), - SERVICE("service"); + SERVICE("service"), + TENANT_ID("tenant_id"); private final String name; @@ -55,9 +57,26 @@ public static Map setValuesFromLambdaContext(Context context) { hashMap.put(FUNCTION_ARN.name, context.getInvokedFunctionArn()); hashMap.put(FUNCTION_MEMORY_SIZE.name, String.valueOf(context.getMemoryLimitInMB())); hashMap.put(FUNCTION_REQUEST_ID.name, String.valueOf(context.getAwsRequestId())); + + // Add TenantId if available (Lambda Tenant Isolation feature) + String tenantId = getTenantId(context); + if (tenantId != null) { + hashMap.put(TENANT_ID.name, tenantId); + } return hashMap; } + + private static String getTenantId(Context context) { + try { + Method getTenantIdMethod = context.getClass().getMethod("getTenantId"); + Object tenantId = getTenantIdMethod.invoke(context); + return tenantId != null ? tenantId.toString() : null; + } catch (Exception e) { + // TenantId method not available or failed to invoke + return null; + } + } public String getName() { return name; From 2f922a05e7c4dd8b7547b1b4a022f7af4a17f95f Mon Sep 17 00:00:00 2001 From: Jeevan Yewale Date: Wed, 28 Jan 2026 19:38:52 +0530 Subject: [PATCH 2/2] Remove reflection and add unit tests for TenantId logging --- .../common/stubs/TestLambdaContext.java | 5 +++++ .../internal/LambdaJsonEncoderTest.java | 4 ++-- .../internal/PowertoolsLoggedFields.java | 19 +------------------ 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java index 6b66b66b7..f54db723b 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/stubs/TestLambdaContext.java @@ -74,4 +74,9 @@ public int getMemoryLimitInMB() { public LambdaLogger getLogger() { return null; } + + @Override + public String getTenantId() { + return "test-tenant"; + } } 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..9bfd354a8 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 @@ -109,7 +109,7 @@ void shouldLogInJsonFormat() { // THEN File logFile = new File("target/logfile.json"); assertThat(contentOf(logFile)).contains( - "{\"level\":\"DEBUG\",\"message\":\"Test debug event\",\"cold_start\":true,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":1,\"service\":\"testLogback\",\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\",\"timestamp\":"); + "{\"level\":\"DEBUG\",\"message\":\"Test debug event\",\"cold_start\":true,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":1,\"service\":\"testLogback\",\"tenant_id\":\"test-tenant\",\"xray_trace_id\":\"1-63441c4a-abcdef012345678912345678\",\"myKey\":\"myValue\",\"timestamp\":"); } @Test @@ -201,7 +201,7 @@ void shouldNotLogPowertoolsInfo() { // THEN assertThat(result).contains( - "{\"level\":\"INFO\",\"message\":\"message\",\"cold_start\":false,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":1,\"sampling_rate\":0.2,\"service\":\"Service\",\"timestamp\":"); + "{\"level\":\"INFO\",\"message\":\"message\",\"cold_start\":false,\"function_arn\":\"arn:aws:lambda:us-east-1:123456789012:function:test\",\"function_memory_size\":128,\"function_name\":\"test-function\",\"function_request_id\":\"test-request-id\",\"function_version\":1,\"sampling_rate\":0.2,\"service\":\"Service\",\"tenant_id\":\"test-tenant\",\"timestamp\":"); // WHEN (powertoolsInfo = false) encoder.setIncludePowertoolsInfo(false); diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java index 7b08021d8..5c351b57e 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsLoggedFields.java @@ -15,7 +15,6 @@ package software.amazon.lambda.powertools.logging.internal; import com.amazonaws.services.lambda.runtime.Context; -import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -57,26 +56,10 @@ public static Map setValuesFromLambdaContext(Context context) { hashMap.put(FUNCTION_ARN.name, context.getInvokedFunctionArn()); hashMap.put(FUNCTION_MEMORY_SIZE.name, String.valueOf(context.getMemoryLimitInMB())); hashMap.put(FUNCTION_REQUEST_ID.name, String.valueOf(context.getAwsRequestId())); - - // Add TenantId if available (Lambda Tenant Isolation feature) - String tenantId = getTenantId(context); - if (tenantId != null) { - hashMap.put(TENANT_ID.name, tenantId); - } + hashMap.put(TENANT_ID.name, context.getTenantId()); return hashMap; } - - private static String getTenantId(Context context) { - try { - Method getTenantIdMethod = context.getClass().getMethod("getTenantId"); - Object tenantId = getTenantIdMethod.invoke(context); - return tenantId != null ? tenantId.toString() : null; - } catch (Exception e) { - // TenantId method not available or failed to invoke - return null; - } - } public String getName() { return name;