Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
},
{
"name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor",
"fields":[{"name":"IS_COLD_START"},{"name":"SERVICE_NAME"}]
"fields":[{"name":"isColdStart"},{"name":"serviceName"}]
}
]
]
4 changes: 4 additions & 0 deletions powertools-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>utils-lite</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@

package software.amazon.lambda.powertools.common.internal;

public class LambdaConstants {
public final class LambdaConstants {
private LambdaConstants() {
// Constant holder class
}

public static final String LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME";
public static final String AWS_REGION_ENV = "AWS_REGION";
public static final String X_AMZN_TRACE_ID = "_X_AMZN_TRACE_ID";
public static final String XRAY_TRACE_HEADER = "com.amazonaws.xray.traceHeader";
public static final String AWS_LAMBDA_X_TRACE_ID = "AWS_LAMBDA_X_TRACE_ID";
public static final String AWS_SAM_LOCAL = "AWS_SAM_LOCAL";
public static final String ROOT_EQUALS = "Root=";
public static final String POWERTOOLS_SERVICE_NAME = "POWERTOOLS_SERVICE_NAME";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,33 @@
import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getProperty;
import static software.amazon.lambda.powertools.common.internal.SystemWrapper.getenv;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;

import org.aspectj.lang.ProceedingJoinPoint;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;

import software.amazon.awssdk.utilslite.SdkInternalThreadLocal;

public final class LambdaHandlerProcessor {

// SERVICE_NAME cannot be final for testing purposes
private static String SERVICE_NAME = calculateServiceName();
// serviceName cannot be final for testing purposes
private static String serviceName = calculateServiceName();

private static Boolean IS_COLD_START = null;
private static Boolean isColdStart = null;

private LambdaHandlerProcessor() {
// Hide default constructor
}

private static String calculateServiceName() {
return null != getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)
? getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME) : LambdaConstants.SERVICE_UNDEFINED;
? getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)
: LambdaConstants.SERVICE_UNDEFINED;
}

public static boolean isHandlerMethod(final ProceedingJoinPoint pjp) {
Expand Down Expand Up @@ -79,35 +84,41 @@
}

public static String serviceName() {
return SERVICE_NAME;
return serviceName;
}

// Method used for testing purposes
protected static void resetServiceName() {

Check failure on line 91 in powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

Avoid protected methods in a final class that doesnt extend anything other than Object. Change to private or package access.

Do not use protected methods in most final classes since they cannot be subclassed. This should only be allowed in final classes that extend other classes with protected methods (whose visibility cannot be reduced). Clarify your intent by using private or package access modifiers instead. AvoidProtectedMethodInFinalClassNotExtending (Priority: 1, Ruleset: Code Style) https://docs.pmd-code.org/snapshot/pmd_rules_java_codestyle.html#avoidprotectedmethodinfinalclassnotextending
SERVICE_NAME = calculateServiceName();
serviceName = calculateServiceName();
}

public static boolean isColdStart() {
return IS_COLD_START == null;
return isColdStart == null;
}

public static void coldStartDone() {
IS_COLD_START = false;
isColdStart = false;
}

public static boolean isSamLocal() {
return "true".equals(getenv(LambdaConstants.AWS_SAM_LOCAL));
}

public static Optional<String> getXrayTraceId() {
String X_AMZN_TRACE_ID = getenv(LambdaConstants.X_AMZN_TRACE_ID);
// Try SdkInternalThreadLocal first
String traceId = SdkInternalThreadLocal.get(LambdaConstants.AWS_LAMBDA_X_TRACE_ID);

// Fallback to environment based approach
if (traceId == null) {
traceId = getenv(LambdaConstants.X_AMZN_TRACE_ID);
}
// For the Java Lambda 17+ runtime, the Trace ID is set as a System Property
if (X_AMZN_TRACE_ID == null) {
X_AMZN_TRACE_ID = getProperty(LambdaConstants.XRAY_TRACE_HEADER);
if (traceId == null) {
traceId = getProperty(LambdaConstants.XRAY_TRACE_HEADER);
}

if (X_AMZN_TRACE_ID != null) {
return of(X_AMZN_TRACE_ID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, ""));
if (traceId != null) {
return of(traceId.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, ""));
}
return empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,26 @@

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.ClearEnvironmentVariable;
import org.junitpioneer.jupiter.ClearSystemProperty;
import org.junitpioneer.jupiter.SetEnvironmentVariable;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;

import software.amazon.awssdk.utilslite.SdkInternalThreadLocal;
import software.amazon.lambda.powertools.common.stubs.TestLambdaContext;

class LambdaHandlerProcessorTest {

@AfterEach
void cleanup() {
SdkInternalThreadLocal.clear();
}

@Test
void isHandlerMethod_shouldRecognizeRequestHandler() {
Context context = new TestLambdaContext();
Expand Down Expand Up @@ -159,6 +167,24 @@ void getXrayTraceId_notPresent() {
assertThat(isXRayTraceIdPresent).isFalse();
}

@Test
@ClearEnvironmentVariable(key = LambdaConstants.X_AMZN_TRACE_ID)
@ClearSystemProperty(key = LambdaConstants.XRAY_TRACE_HEADER)
void getXrayTraceId_fromSdkInternalThreadLocal() {
// Verify no trace ID initially
assertThat(LambdaHandlerProcessor.getXrayTraceId()).isEmpty();

// Set trace ID in SdkInternalThreadLocal
String expectedTraceId = "1-5759e988-bd862e3fe1be46a994272793";
SdkInternalThreadLocal.put(LambdaConstants.AWS_LAMBDA_X_TRACE_ID,
"Root=" + expectedTraceId + ";Parent=53995c3f42cd8ad8;Sampled=1");

// Verify trace ID is now present
Optional<String> traceId = LambdaHandlerProcessor.getXrayTraceId();
assertThat(traceId).isPresent();
assertThat(traceId.get()).isEqualTo(expectedTraceId);
}

@Test
void extractContext_fromRequestHandler() {
Object[] args = { new Object(), new TestLambdaContext() };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@
},
{
"name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor",
"fields":[{"name":"IS_COLD_START"}]
"fields":[{"name":"isColdStart"}]
},
{
"name":"software.amazon.lambda.powertools.logging.log4j.BufferingAppender",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class PowerToolsResolverFactoryTest {
@BeforeEach
void setUp() throws IllegalAccessException, IOException {
MDC.clear();
writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true);
writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true);
context = new TestLambdaContext();
// Make sure file is cleaned up before running tests
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
},
{
"name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor",
"fields":[{"name":"IS_COLD_START"}]
"fields":[{"name":"isColdStart"}]
},
{
"name":"software.amazon.lambda.powertools.logging.logback.BufferingAppender",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class LambdaEcsEncoderTest {
@BeforeEach
void setUp() throws IllegalAccessException, IOException {
MDC.clear();
writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true);
writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true);
context = new TestLambdaContext();
// Make sure file is cleaned up before running tests
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class LambdaJsonEncoderTest {
@BeforeEach
void setUp() throws IllegalAccessException, IOException {
MDC.clear();
writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true);
writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true);
context = new TestLambdaContext();
// Make sure file is cleaned up before running tests
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
},
{
"name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor",
"fields":[{"name":"IS_COLD_START"}, {"name":"SERVICE_NAME"}]
"fields":[{"name":"isColdStart"}, {"name":"serviceName"}]
},
{
"name":"software.amazon.lambda.powertools.logging.argument.StructuredArgumentsTest",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class LambdaLoggingAspectTest {
@BeforeEach
void setUp() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException {
MDC.clear();
writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true);
writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true);
context = new TestLambdaContext();
requestHandler = new PowertoolsLogEnabled();
requestStreamHandler = new PowertoolsLogEnabledForStream();
Expand Down Expand Up @@ -418,7 +418,7 @@ void shouldHaveNoEffectIfNotUsedOnLambdaHandler() {
@Test
void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException {
// GIVEN
writeStaticField(LambdaHandlerProcessor.class, "SERVICE_NAME", "testService", true);
writeStaticField(LambdaHandlerProcessor.class, "serviceName", "testService", true);

// WHEN
requestHandler.handleRequest(new Object(), context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
},
{
"name": "software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor",
"fields": [{ "name": "IS_COLD_START" }],
"fields": [{ "name": "isColdStart" }],
"methods": [{ "name": "resetServiceName", "parameterTypes": [] }]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
*/
class ConfigurationPrecedenceTest {

private final PrintStream standardOut = System.out;

Check failure on line 47 in powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

The final field standardOut could be made static

If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead in each object at runtime. FinalFieldCouldBeStatic (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#finalfieldcouldbestatic
private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
private final ObjectMapper objectMapper = new ObjectMapper();

Expand All @@ -52,13 +52,13 @@
void setUp() throws Exception {
System.setOut(new PrintStream(outputStreamCaptor));

// Reset LambdaHandlerProcessor's SERVICE_NAME
// Reset LambdaHandlerProcessor's serviceName
Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName");
resetServiceName.setAccessible(true);
resetServiceName.invoke(null);

// Reset IS_COLD_START
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START");
// Reset isColdStart
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart");
coldStartField.setAccessible(true);
coldStartField.set(null, null);
}
Expand Down Expand Up @@ -183,7 +183,7 @@
assertThat(rootNode.has("Service")).isFalse();
}

private static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> {

Check failure on line 186 in powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

This class has only private constructors and may be final

Reports classes that may be made final because they cannot be extended from outside their compilation unit anyway. This is because all their constructors are private, so a subclass could not call the super constructor. ClassWithOnlyPrivateConstructorsShouldBeFinal (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#classwithonlyprivateconstructorsshouldbefinal
@Override
@FlushMetrics(namespace = "AnnotationNamespace", service = "AnnotationService")
public String handleRequest(Map<String, Object> input, Context context) {
Expand All @@ -193,7 +193,7 @@
}
}

private static class HandlerWithDefaultMetricsAnnotation implements RequestHandler<Map<String, Object>, String> {

Check failure on line 196 in powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

This class has only private constructors and may be final

Reports classes that may be made final because they cannot be extended from outside their compilation unit anyway. This is because all their constructors are private, so a subclass could not call the super constructor. ClassWithOnlyPrivateConstructorsShouldBeFinal (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#classwithonlyprivateconstructorsshouldbefinal
@Override
@FlushMetrics
public String handleRequest(Map<String, Object> input, Context context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
private static final String TEST_NAMESPACE = "TestNamespace";
private static final String TEST_SERVICE = "TestService";

private final PrintStream standardOut = System.out;

Check failure on line 43 in powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

The final field standardOut could be made static

If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead in each object at runtime. FinalFieldCouldBeStatic (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#finalfieldcouldbestatic
private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
private final ObjectMapper objectMapper = new ObjectMapper();

Expand All @@ -48,13 +48,13 @@
void setUp() throws Exception {
System.setOut(new PrintStream(outputStreamCaptor));

// Reset LambdaHandlerProcessor's SERVICE_NAME
// Reset LambdaHandlerProcessor's serviceName
Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName");
resetServiceName.setAccessible(true);
resetServiceName.invoke(null);

// Reset IS_COLD_START
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START");
// Reset isColdStart
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart");
coldStartField.setAccessible(true);
coldStartField.set(null, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ void setUp() throws Exception {
outputStreamCaptor = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStreamCaptor));

// Reset LambdaHandlerProcessor's SERVICE_NAME
// Reset LambdaHandlerProcessor's serviceName
Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName");
resetServiceName.setAccessible(true);
resetServiceName.invoke(null);

// Reset IS_COLD_START
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START");
// Reset isColdStart
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart");
coldStartField.setAccessible(true);
coldStartField.set(null, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ class EmfMetricsLoggerTest {

@BeforeEach
void setUp() throws Exception {
// Reset LambdaHandlerProcessor's SERVICE_NAME
// Reset LambdaHandlerProcessor's serviceName
Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName");
resetServiceName.setAccessible(true);
resetServiceName.invoke(null);

// Reset IS_COLD_START
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START");
// Reset isColdStart
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart");
coldStartField.setAccessible(true);
coldStartField.set(null, null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

class LambdaMetricsAspectTest {

private final PrintStream standardOut = System.out;

Check failure on line 44 in powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

The final field standardOut could be made static

If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead in each object at runtime. FinalFieldCouldBeStatic (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#finalfieldcouldbestatic
private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
private final ObjectMapper objectMapper = new ObjectMapper();

Expand All @@ -49,13 +49,13 @@
void setUp() throws Exception {
System.setOut(new PrintStream(outputStreamCaptor));

// Reset LambdaHandlerProcessor's SERVICE_NAME
// Reset LambdaHandlerProcessor's serviceName
Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName");
resetServiceName.setAccessible(true);
resetServiceName.invoke(null);

// Reset IS_COLD_START
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START");
// Reset isColdStart
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("isColdStart");
coldStartField.setAccessible(true);
coldStartField.set(null, null);
}
Expand Down Expand Up @@ -216,7 +216,7 @@
JsonNode dimensions = coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0);
boolean hasFunctionName = false;
for (JsonNode dimension : dimensions) {
if (dimension.asText().equals("FunctionName")) {

Check failure on line 219 in powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

Position literals first in String comparisons

Position literals first in all String comparisons, if the second argument is null then NullPointerExceptions can be avoided, they will just return false. Note that switching literal positions for compareTo and compareToIgnoreCase may change the result, see examples. Note that compile-time constant strings are treated like literals. This is because they are inlined into the class file, are necessarily non-null, and therefore cannot cause an NPE at runtime. LiteralsFirstInComparisons (Priority: 1, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#literalsfirstincomparisons
hasFunctionName = true;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
},
{
"name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor",
"fields":[{"name":"IS_COLD_START"}]
"fields":[{"name":"isColdStart"}]
},
{
"name":"software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabled",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class LambdaTracingAspectTest {

@BeforeEach
void setUp() throws IllegalAccessException {
writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true);
writeStaticField(LambdaHandlerProcessor.class, "isColdStart", null, true);
context = new TestLambdaContext();
requestHandler = new PowerTracerToolEnabled();
streamHandler = new PowerTracerToolEnabledForStream();
Expand Down
Loading