diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml
index b22dd80bc..eb3cc053d 100644
--- a/.github/workflows/pr_build.yml
+++ b/.github/workflows/pr_build.yml
@@ -49,7 +49,7 @@ jobs:
strategy:
max-parallel: 5
matrix:
- java: [8, 11, 17, 21, 15, 16, 18, 19, 20]
+ java: [8, 11, 17, 21]
name: Java ${{ matrix.java }}
env:
JAVA: ${{ matrix.java }}
diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml
index 0cd9fee43..60048abdc 100644
--- a/.github/workflows/run-e2e-tests.yml
+++ b/.github/workflows/run-e2e-tests.yml
@@ -33,13 +33,13 @@ jobs:
e2e:
runs-on: ubuntu-latest
strategy:
- max-parallel: 3
+ max-parallel: 4
matrix:
- java: [ 8, 11, 17 ]
+ java: [ 8, 11, 17, 21 ]
name: End-to-end tests java${{ matrix.java }}
env:
- JAVA_VERSION: ${{ matrix.java }}
AWS_DEFAULT_REGION: eu-west-1
+ JAVA_VERSION: ${{ matrix.java }}
permissions:
id-token: write # needed to interact with GitHub's OIDC Token endpoint.
contents: read
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbb90ce3f..4b9f664fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,37 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo
## [Unreleased]
+## [1.18.0] - 2023-11-16
+
+### Added
+
+* feat: add support for [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced) (#1514) by @jeromevdl
+* feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) by @AlexeySoshin
+
+### Maintenance
+
+* fix: json schema 403 error (#1457) by @jeromevdl
+* fix: array jmespath fail in idempotency module (#1420) by @jeromevdl
+* chore: java21 support in our build (#1488) by @jeromevdl
+* chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) by @jdoherty
+* fix: null namespace should fallback to default namespace (#1506) by @jeromevdl
+* fix: get trace id from system property when env var is not set (#1503) by @mriccia
+* chore: artifacts size on good branches (#1493) by @jeromevdl
+* fix: enforce jackson databind version (#1472) by @jeromevdl
+* chore: add missing projects and improve workflow (#1487) by @jeromevdl
+* chore: Reporting size of the jars in GitHub comments (#1196) by @jeromevdl
+* Deps: Bump third party dependencies to the latest versions.
+
+### Documentation
+
+* docs(customer-reference): add Vertex Pharmaceuticals as a customer reference (#1486) by @scottgerring
+* docs: Adding Kotlin example. (#1454) by @jasoniharris
+* docs: Terraform example (#1478) by @skal111
+* docs: Add Serveless Framework example (#1363) by @AlexeySoshin
+* docs: Fix link to SQS large message migration guide (#1422) by @scottgerring
+* docs(logging): correct log example keys (#1411) by @walmsles
+* docs: Update gradle configuration readme (#1359) by @scottgerring
+
## [1.17.0] - 2023-08-21
### Added
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8db303737..c4330cf8e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -55,8 +55,7 @@ We strongly recommend installing the CheckStyle-IDEA plugin and apply the provid
2. After installing the plugin, open the preferences (`⌘,` on macOS, or `Ctrl+Alt+S` on Windows/Linux) and search for _Code Style_. Click on the gear icon near the scheme and import checkstyle configuration. Click on "Apply" and "OK".

-3. Select the code you've created (module, package, class) and reformat code: `⌘⌥L` (macOS), or `Ctrl+Alt+L` (Windows/Linux):
-
+3. Select the code you've created (module, package, class) and reformat code: `⌘⌥L` (macOS), or `Ctrl+Alt+L` (Windows/Linux).
4. Apply the reformat, optimize imports, rearrange and cleanup to your code and only to java files:

diff --git a/README.md b/README.md
index e89e32994..aa4b872b7 100644
--- a/README.md
+++ b/README.md
@@ -11,28 +11,28 @@ Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless
**[📜Documentation](https://docs.powertools.aws.dev/lambda-java/)** | **[Feature request](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=feature-request%2C+triage&template=feature_request.md&title=)** | **[🐛Bug Report](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=bug%2C+triage&template=bug_report.md&title=)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-aws-lambda-powertools-java/)**
-### Installation
+## Installation
Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it
-#### Maven:
+### Maven:
```xml
...
software.amazon.lambdapowertools-tracing
- 1.17.0
+ 1.18.0software.amazon.lambdapowertools-logging
- 1.17.0
+ 1.18.0software.amazon.lambdapowertools-metrics
- 1.17.0
+ 1.18.0
...
@@ -190,9 +190,36 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam
```
+### Java Compatibility
+Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the
+[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html).
+For the modules that provide annotations, Powertools for AWS Lambda (Java) leverages the **aspectj** library.
+You may need to add the good version of `aspectjrt` to your dependencies based on the JDK used for building your function:
+
+```xml
+
+ org.aspectj
+ aspectjrt
+ 1.9.??
+
+```
+
+
+ JDK - aspectj dependency matrix
+
+| JDK version | aspectj version |
+|-------------|-----------------|
+| `1.8` | `1.9.7` |
+| `11-17` | `1.9.20.1` |
+| `21` | `1.9.21` |
+
+More info [here](https://github.com/aws-powertools/powertools-lambda-java/pull/1519/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R191).
+
+
+
## Examples
-See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/v1.17.0/examples)** for example projects showcasing usage of different utilities.
+See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/v1.18.0/examples)** for example projects showcasing usage of different utilities.
Have a demo project to contribute which showcase usage of different utilities from powertools? We are happy to accept it [here](CONTRIBUTING.md#security-issue-notifications).
diff --git a/docs/core/logging.md b/docs/core/logging.md
index 2df9a4529..70781b1b2 100644
--- a/docs/core/logging.md
+++ b/docs/core/logging.md
@@ -217,9 +217,7 @@ Key | Type | Example | Description
## Capturing context Lambda info
-You can enrich your structured logs with key Lambda context information via `logEvent` annotation parameter.
-You can also explicitly log any incoming event using `logEvent` param. Refer [Override default object mapper](#override-default-object-mapper)
-to customise what is logged.
+When debugging in non-production environments, you can instruct Logger to log the incoming event with `@Logger(logEvent = true)` or via `POWERTOOLS_LOGGER_LOG_EVENT=true` environment variable.
!!! warning
Log event is disabled by default to prevent sensitive info being logged.
@@ -265,7 +263,7 @@ to customise what is logged.
}
```
-### Customising fields in logs
+### Customising fields in logs
- Utility by default emits `timestamp` field in the logs in format `yyyy-MM-dd'T'HH:mm:ss.SSSZz` and in system default timezone.
If you need to customize format and timezone, you can do so by configuring `log4j2.component.properties` and configuring properties as shown in example below:
@@ -598,6 +596,54 @@ via `samplingRate` attribute on annotation.
POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5
```
+## AWS Lambda Advanced Logging Controls (ALC)
+
+!!!question "When is it useful?"
+ When you want to set a logging policy to drop informational or verbose logs for one or all AWS Lambda functions, regardless of runtime and logger used.
+
+
+With [AWS Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced){target="_blank"}, you can enforce a minimum log level that Lambda will accept from your application code.
+
+When enabled, you should keep `Logger` and ALC log level in sync to avoid data loss.
+
+Here's a sequence diagram to demonstrate how ALC will drop both `INFO` and `DEBUG` logs emitted from `Logger`, when ALC log level is stricter than `Logger`.
+
+
+```mermaid
+sequenceDiagram
+ participant Lambda service
+ participant Lambda function
+ participant Application Logger
+
+ Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN"
+ Note over Application Logger: POWERTOOLS_LOG_LEVEL="DEBUG"
+
+ Lambda service->>Lambda function: Invoke (event)
+ Lambda function->>Lambda function: Calls handler
+ Lambda function->>Application Logger: logger.error("Something happened")
+ Lambda function-->>Application Logger: logger.debug("Something happened")
+ Lambda function-->>Application Logger: logger.info("Something happened")
+ Lambda service--xLambda service: DROP INFO and DEBUG logs
+ Lambda service->>CloudWatch Logs: Ingest error logs
+```
+
+### Priority of log level settings in Powertools for AWS Lambda
+
+We prioritise log level settings in this order:
+
+1. `AWS_LAMBDA_LOG_LEVEL` environment variable
+2. `POWERTOOLS_LOG_LEVEL` environment variable
+
+If you set `Logger` level lower than ALC, we will emit a warning informing you that your messages will be discarded by Lambda.
+
+> **NOTE**
+>
+> With ALC enabled, we are unable to increase the minimum log level below the `AWS_LAMBDA_LOG_LEVEL` environment variable value, see [AWS Lambda service documentation](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-log-level){target="_blank"} for more details.
+
+### Timestamp format
+
+When the Advanced Logging Controls feature is enabled, Powertools for AWS Lambda must comply with the timestamp format required by AWS Lambda, which is [RFC3339](https://www.rfc-editor.org/rfc/rfc3339).
+In this case the format will be `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`.
## Upgrade to JsonTemplateLayout from deprecated LambdaJsonLayout configuration in log4j2.xml
diff --git a/docs/index.md b/docs/index.md
index 6af4c5e8d..884e02476 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -11,7 +11,7 @@ Powertools for AWS Lambda (Java) is a suite of utilities for AWS Lambda Function
Powertools for AWS Lambda is also available for [Python](https://docs.powertools.aws.dev/lambda/python/latest/){target="_blank"}, [TypeScript](https://docs.powertools.aws.dev/lambda/typescript/latest/){target="_blank"}, and [.NET](https://docs.powertools.aws.dev/lambda/dotnet/){target="_blank"}
-!!! tip "Looking for a quick run through of the core utilities?"
+???+ tip "Looking for a quick run through of the core utilities?"
Check out [this detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-aws-lambda-powertools-java/) with a practical example. To dive deeper,
the [Powertools for AWS Lambda (Java) workshop](https://catalog.us-east-1.prod.workshops.aws/workshops/a7011c82-e4af-4a52-80fa-fcd61f1dacd9/en-US/introduction) is a great next step.
@@ -274,6 +274,41 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl
Under the hood, `org.codehaus.mojo:aspectj-maven-plugin` is based on AspectJ 1.9.7,
while `dev.aspectj:aspectj-maven-plugin` is based on AspectJ 1.9.8, compiled for Java 11+.
+### Java Compatibility
+Powertools for AWS Lambda (Java) supports all Java version from 8 up to 21 as well as the
+[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html).
+
+For the following modules, Powertools for AWS Lambda (Java) leverages the **aspectj** library to provide annotations:
+- Logging
+- Metrics
+- Tracing
+- Parameters
+- Idempotency
+- Validation
+- Large messages
+
+
+You may need to add the good version of `aspectjrt` to your dependencies based on the jdk used for building your function:
+
+```xml
+
+ org.aspectj
+ aspectjrt
+ 1.9.??
+
+```
+
+Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md) between this library and the JDK:
+
+| JDK version | aspectj version |
+|-------------|-----------------|
+| `1.8` | `1.9.7` |
+| `11-17` | `1.9.20.1` |
+| `21` | `1.9.21` |
+
+_Note: 1.9.21 is not yet available and Java 21 not yet officially supported by aspectj, but you can already use the `1.9.21.M1`_
+
+
## Environment variables
!!! info
@@ -285,5 +320,7 @@ Depending on your version of Java (either Java 1.8 or 11+), the configuration sl
| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) |
| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logging](./core/logging) |
| **POWERTOOLS_LOG_LEVEL** | Sets logging level | [Logging](./core/logging) |
+| **POWERTOOLS_LOGGER_LOG_EVENT** | Enables/Disables whether to log the incoming event when using the aspect | [Logging](./core/logging) |
| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Enables/Disables tracing mode to capture method response | [Tracing](./core/tracing) |
| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Enables/Disables tracing mode to capture method error | [Tracing](./core/tracing) |
+
diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml
index d67077fae..d2b05b122 100644
--- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml
+++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml
@@ -7,7 +7,7 @@
2.0.0-SNAPSHOTUTF-8
- 2.93.0
+ 2.100.0[10.0.0,11.0.0)5.10.0
diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle
index 06aec59e8..e85936350 100644
--- a/examples/powertools-examples-core-utilities/gradle/build.gradle
+++ b/examples/powertools-examples-core-utilities/gradle/build.gradle
@@ -18,6 +18,7 @@ compileJava {
}
repositories {
+ mavenLocal()
mavenCentral()
}
@@ -27,8 +28,8 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2'
implementation 'com.amazonaws:aws-lambda-java-events:3.11.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2'
- aspect 'software.amazon.lambda:powertools-tracing:1.17.0'
- aspect 'software.amazon.lambda:powertools-logging:1.17.0'
- aspect 'software.amazon.lambda:powertools-metrics:1.17.0'
+ aspect 'software.amazon.lambda:powertools-tracing:2.0.0-SNAPSHOT'
+ aspect 'software.amazon.lambda:powertools-logging:2.0.0-SNAPSHOT'
+ aspect 'software.amazon.lambda:powertools-metrics:2.0.0-SNAPSHOT'
}
diff --git a/mkdocs.yml b/mkdocs.yml
index cde47c815..a8569f664 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -85,7 +85,7 @@ extra_javascript:
extra:
powertools:
- version: 1.17.0 # to update after each release (we do not want snapshot version here)
+ version: 1.18.0 # to update after each release (we do not want snapshot version here)
repo_url: https://github.com/aws-powertools/powertools-lambda-java
edit_uri: edit/main/docs
diff --git a/pom.xml b/pom.xml
index 15de3af0b..cad72dee1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,7 +73,6 @@
1.82.20.02.15.3
- 1.9.72.21.02.14.02.1.3
@@ -82,6 +81,7 @@
3.11.21.1.23.11.0
+ 1.9.71.13.13.1.20.8.10
@@ -514,6 +514,7 @@
+
jdk16
diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml
index 79864a64a..0e6763e25 100644
--- a/powertools-cloudformation/pom.xml
+++ b/powertools-cloudformation/pom.xml
@@ -76,6 +76,10 @@
com.fasterxml.jackson.corejackson-databind
+
+ org.aspectj
+ aspectjrt
+
@@ -107,14 +111,6 @@
-
- dev.aspectj
- aspectj-maven-plugin
- ${aspectj-maven-plugin.version}
-
- true
-
- org.apache.maven.pluginsmaven-checkstyle-plugin
diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java
index f43cd52a3..bdcbdc010 100644
--- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java
+++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java
@@ -24,6 +24,7 @@ public class LambdaConstants {
@Deprecated
public static final String ON_DEMAND = "on-demand";
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_SAM_LOCAL = "AWS_SAM_LOCAL";
public static final String ROOT_EQUALS = "Root=";
public static final String POWERTOOLS_SERVICE_NAME = "POWERTOOLS_SERVICE_NAME";
diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java
index 1a2b29c2c..a2830e467 100644
--- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java
+++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java
@@ -16,6 +16,7 @@
import static java.util.Optional.empty;
import static java.util.Optional.of;
+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;
@@ -46,6 +47,13 @@ public static boolean isHandlerMethod(final ProceedingJoinPoint pjp) {
return placedOnRequestHandler(pjp) || placedOnStreamHandler(pjp);
}
+ /**
+ * The class needs to implement RequestHandler interface
+ * The function needs to have exactly two arguments
+ * The second argument needs to be of type com.amazonaws.services.lambda.runtime.Context
+ * @param pjp
+ * @return
+ */
public static boolean placedOnRequestHandler(final ProceedingJoinPoint pjp) {
return RequestHandler.class.isAssignableFrom(pjp.getSignature().getDeclaringType())
&& pjp.getArgs().length == 2
@@ -93,7 +101,12 @@ public static boolean isSamLocal() {
}
public static Optional getXrayTraceId() {
- final String X_AMZN_TRACE_ID = getenv(LambdaConstants.X_AMZN_TRACE_ID);
+ String X_AMZN_TRACE_ID = 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 (X_AMZN_TRACE_ID != null) {
return of(X_AMZN_TRACE_ID.split(";")[0].replace(LambdaConstants.ROOT_EQUALS, ""));
}
diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java
index c537283b5..6dc4e9d9f 100644
--- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java
+++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/SystemWrapper.java
@@ -21,4 +21,8 @@ private SystemWrapper() {
public static String getenv(String name) {
return System.getenv(name);
}
+
+ public static String getProperty(String name) {
+ return System.getProperty(name);
+ }
}
diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml
index 0ee014116..05e91d8e3 100644
--- a/powertools-e2e-tests/handlers/logging/pom.xml
+++ b/powertools-e2e-tests/handlers/logging/pom.xml
@@ -17,6 +17,10 @@
software.amazon.lambdapowertools-logging
+
+ org.aspectj
+ aspectjrt
+ com.amazonawsaws-lambda-java-events
diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml
index fbe2e6d8b..1bda3caa5 100644
--- a/powertools-e2e-tests/handlers/pom.xml
+++ b/powertools-e2e-tests/handlers/pom.xml
@@ -43,7 +43,11 @@
pomimport
-
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+ software.amazon.lambdapowertools-logging
@@ -99,11 +103,6 @@
log4j-slf4j2-impl${log4j.version}
-
- org.aspectj
- aspectjrt
- ${aspectj.version}
-
@@ -132,11 +131,11 @@
-
- org.apache.logging.log4j
- log4j-transform-maven-shade-plugin-extensions
- 0.1.0
-
+
+ org.apache.logging.log4j
+ log4j-transform-maven-shade-plugin-extensions
+ 0.1.0
+
@@ -154,6 +153,7 @@
aspectj-maven-plugin${aspectj.plugin.version}
+ true${maven.compiler.source}${maven.compiler.target}${maven.compiler.target}
@@ -162,7 +162,6 @@
- process-sourcescompiletest-compile
@@ -182,15 +181,34 @@
+
- jdk8
+ jdk8to16
- (,11)
+ [1.8,16]1.9.7
+
+ jdk17to20
+
+ [17,20]
+
+
+ 1.9.20.1
+
+
+
+ jdk21
+
+ [21,)
+
+
+ 1.9.21.M1
+
+
diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml
index c512beb3a..1f5bd6347 100644
--- a/powertools-e2e-tests/pom.xml
+++ b/powertools-e2e-tests/pom.xml
@@ -31,7 +31,7 @@
1.81.810.3.0
- 2.100.0
+ 2.109.0
diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java
index e8ee3ca5c..ef342ea13 100644
--- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java
+++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageIdempotentE2ET.java
@@ -107,7 +107,7 @@ public static void tearDown() {
@Test
public void test_ttlNotExpired_doesNotInsertInDDB_ttlExpired_insertInDDB() throws InterruptedException,
IOException {
- int waitMs = 10000;
+ int waitMs = 15000;
// GIVEN
InputStream inputStream = this.getClass().getResourceAsStream("/large_sqs_message.txt");
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 f958970d8..b060879d3 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
@@ -46,6 +46,7 @@ public class LoggingE2ET {
public static void setup() {
infrastructure = Infrastructure.builder()
.testName(LoggingE2ET.class.getSimpleName())
+ .tracing(true)
.pathToFunction("logging")
.environmentVariables(
Stream.of(new String[][] {
@@ -83,6 +84,7 @@ public void test_logInfoWithAdditionalKeys() throws JsonProcessingException {
assertThat(jsonNode.get("message").asText()).isEqualTo("New Order");
assertThat(jsonNode.get("orderId").asText()).isEqualTo(orderId);
assertThat(jsonNode.get("coldStart").asBoolean()).isTrue();
+ assertThat(jsonNode.get("xray_trace_id").asText()).isNotBlank();
assertThat(jsonNode.get("function_request_id").asText()).isEqualTo(invocationResult1.getRequestId());
// second call should not be cold start
diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java
index b1fab2883..28a0f2bb4 100644
--- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java
+++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java
@@ -204,13 +204,13 @@ public void destroy() {
private Stack createStackWithLambda() {
boolean createTableForAsyncTests = false;
Stack stack = new Stack(app, stackName);
+
List packagingInstruction = Arrays.asList(
"/bin/sh",
"-c",
"cd " + pathToFunction +
" && timeout -s SIGKILL 5m mvn clean install -ff " +
" -Dmaven.test.skip=true " +
- " -Dmaven.resources.skip=true " +
" -Dmaven.compiler.source=" + runtime.getMvnProperty() +
" -Dmaven.compiler.target=" + runtime.getMvnProperty() +
" && cp /asset-input/" + pathToFunction + "/target/function.jar /asset-output/"
@@ -506,27 +506,30 @@ public static class Builder {
private String ddbStreamsTableName;
private Builder() {
- getJavaRuntime();
+ runtime = mapRuntimeVersion("JAVA_VERSION");
}
- /**
- * Retrieve the java runtime to use for the lambda function.
- */
- private void getJavaRuntime() {
- String javaVersion = System.getenv("JAVA_VERSION"); // must be set in GitHub actions
+
+
+ private JavaRuntime mapRuntimeVersion(String environmentVariableName) {
+ String javaVersion = System.getenv(environmentVariableName); // must be set in GitHub actions
+ JavaRuntime ret = null;
if (javaVersion == null) {
- throw new IllegalArgumentException("JAVA_VERSION is not set");
+ throw new IllegalArgumentException(environmentVariableName + " is not set");
}
if (javaVersion.startsWith("8")) {
- runtime = JavaRuntime.JAVA8AL2;
+ ret = JavaRuntime.JAVA8AL2;
} else if (javaVersion.startsWith("11")) {
- runtime = JavaRuntime.JAVA11;
+ ret = JavaRuntime.JAVA11;
} else if (javaVersion.startsWith("17")) {
- runtime = JavaRuntime.JAVA17;
+ ret = JavaRuntime.JAVA17;
+ } else if (javaVersion.startsWith("21")) {
+ ret = JavaRuntime.JAVA21;
} else {
throw new IllegalArgumentException("Unsupported Java version " + javaVersion);
}
- LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime());
+ LOG.debug("Java Version set to {}, using runtime variable {}", ret, javaVersion);
+ return ret;
}
public Infrastructure build() {
diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java
index c50fcab84..c75682949 100644
--- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java
+++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java
@@ -20,7 +20,8 @@ public enum JavaRuntime {
JAVA8("java8", Runtime.JAVA_8, "1.8"),
JAVA8AL2("java8.al2", Runtime.JAVA_8_CORRETTO, "1.8"),
JAVA11("java11", Runtime.JAVA_11, "11"),
- JAVA17("java17", Runtime.JAVA_17, "17");
+ JAVA17("java17", Runtime.JAVA_17, "17"),
+ JAVA21("java21", Runtime.JAVA_21, "21");
private final String runtime;
private final Runtime cdkRuntime;
diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml
index 61294b996..f7f44dc99 100644
--- a/powertools-idempotency/pom.xml
+++ b/powertools-idempotency/pom.xml
@@ -86,6 +86,10 @@
url-connection-client${aws.sdk.version}
+
+ org.aspectj
+ aspectjrt
+
@@ -108,11 +112,6 @@
commons-lang3test
-
- org.aspectj
- aspectjweaver
- test
- org.assertjassertj-core
diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml
index 12ebdc335..21a221967 100644
--- a/powertools-logging/pom.xml
+++ b/powertools-logging/pom.xml
@@ -133,6 +133,16 @@
org.apache.maven.pluginsmaven-checkstyle-plugin
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+
+ JSON
+
+
+
\ No newline at end of file
diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java
index 0a36723f6..2e17ce692 100644
--- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java
+++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java
@@ -28,11 +28,13 @@
import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKey;
import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKeys;
import static software.amazon.lambda.powertools.logging.LoggingUtils.objectMapper;
+import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_LEVEL;
import com.amazonaws.services.lambda.runtime.Context;
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -42,6 +44,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Random;
+
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -61,19 +64,37 @@
@DeclarePrecedence("*, software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect")
public final class LambdaLoggingAspect {
private static final Logger LOG = LogManager.getLogger(LambdaLoggingAspect.class);
- private static final Random SAMPLER = new Random();
+ private static final String POWERTOOLS_LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL");
- private static final String LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL");
+ private static final Random SAMPLER = new Random();
private static final String SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE");
+ private static Boolean LOG_EVENT;
private static Level LEVEL_AT_INITIALISATION;
static {
- if (null != LOG_LEVEL) {
- resetLogLevels(Level.getLevel(LOG_LEVEL));
+ if (POWERTOOLS_LOG_LEVEL != null) {
+ Level powertoolsLevel = Level.getLevel(POWERTOOLS_LOG_LEVEL);
+ if (LAMBDA_LOG_LEVEL != null) {
+ Level lambdaLevel = Level.getLevel(LAMBDA_LOG_LEVEL);
+ if (powertoolsLevel.intLevel() > lambdaLevel.intLevel()) {
+ LOG.warn("Current log level ({}) does not match AWS Lambda Advanced Logging Controls minimum log level ({}). This can lead to data loss, consider adjusting them.",
+ POWERTOOLS_LOG_LEVEL, LAMBDA_LOG_LEVEL);
+ }
+ }
+ resetLogLevels(powertoolsLevel);
+ } else if (LAMBDA_LOG_LEVEL != null) {
+ resetLogLevels(Level.getLevel(LAMBDA_LOG_LEVEL));
}
LEVEL_AT_INITIALISATION = LOG.getLevel();
+
+ String logEvent = System.getenv("POWERTOOLS_LOGGER_LOG_EVENT");
+ if (logEvent != null) {
+ LOG_EVENT = Boolean.parseBoolean(logEvent);
+ } else {
+ LOG_EVENT = false;
+ }
}
private static void resetLogLevels(Level logLevel) {
@@ -104,7 +125,9 @@ public Object around(ProceedingJoinPoint pjp,
getXrayTraceId().ifPresent(xRayTraceId -> appendKey("xray_trace_id", xRayTraceId));
- if (logging.logEvent()) {
+ // Check that the environment variable was enabled explicitly
+ // Or that the handler was annotated with @Logging(logEvent = true)
+ if (LOG_EVENT || logging.logEvent()) {
proceedArgs = logEvent(pjp);
}
diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java
new file mode 100644
index 000000000..500b36c95
--- /dev/null
+++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java
@@ -0,0 +1,169 @@
+/*
+ * 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.logging.internal;
+
+import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LAMBDA_LOG_FORMAT;
+import static software.amazon.lambda.powertools.logging.internal.LoggingConstants.LOG_DATE_RFC3339_FORMAT;
+
+import java.util.Locale;
+import java.util.TimeZone;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.time.MutableInstant;
+import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults;
+import org.apache.logging.log4j.layout.template.json.resolver.EventResolver;
+import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig;
+import org.apache.logging.log4j.layout.template.json.util.InstantFormatter;
+import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
+
+/**
+ * Default timestamp used by log4j is not RFC3339, which is used by Lambda internally to filter logs.
+ * When `AWS_LAMBDA_LOG_FORMAT` is set to JSON (i.e. using Lambda logging configuration), we should use the appropriate pattern,
+ * otherwise logs with invalid date format are considered as INFO.
+ * Inspired from org.apache.logging.log4j.layout.template.json.resolver.TimestampResolver
+ *
+ * TODO: remove in v2 an replace with the good pattern in LambdaJsonLayout.json
+ */
+public class LambdaTimestampResolver implements EventResolver {
+
+ private final EventResolver internalResolver;
+
+ public LambdaTimestampResolver(final TemplateResolverConfig config) {
+ final PatternResolverContext patternResolverContext =
+ PatternResolverContext.fromConfig(config);
+ internalResolver = new PatternResolver(patternResolverContext);
+ }
+
+ @Override
+ public void resolve(LogEvent value, JsonWriter jsonWriter) {
+ internalResolver.resolve(value, jsonWriter);
+ }
+
+ static String getName() {
+ return "lambda-timestamp";
+ }
+
+ private static final class PatternResolverContext {
+
+ public static final String PATTERN = "pattern";
+ private final InstantFormatter formatter;
+
+ private final StringBuilder lastFormattedInstantBuffer = new StringBuilder();
+
+ private final MutableInstant lastFormattedInstant = new MutableInstant();
+
+ private PatternResolverContext(
+ final String pattern,
+ final TimeZone timeZone,
+ final Locale locale) {
+ this.formatter = InstantFormatter
+ .newBuilder()
+ .setPattern(pattern)
+ .setTimeZone(timeZone)
+ .setLocale(locale)
+ .build();
+ lastFormattedInstant.initFromEpochSecond(-1, 0);
+ }
+
+ private static PatternResolverContext fromConfig(
+ final TemplateResolverConfig config) {
+ final String pattern = readPattern(config);
+ final TimeZone timeZone = readTimeZone(config);
+ final Locale locale = config.getLocale(new String[]{PATTERN, "locale"});
+ return new PatternResolverContext(pattern, timeZone, locale);
+ }
+
+ private static String readPattern(final TemplateResolverConfig config) {
+ final String format = config.getString(new String[]{PATTERN, "format"});
+ return format != null
+ ? format
+ : getLambdaTimestampFormatOrDefault();
+ }
+
+ private static String getLambdaTimestampFormatOrDefault() {
+ return "JSON".equals(LAMBDA_LOG_FORMAT) ? LOG_DATE_RFC3339_FORMAT :
+ JsonTemplateLayoutDefaults.getTimestampFormatPattern();
+ }
+
+ private static TimeZone readTimeZone(final TemplateResolverConfig config) {
+ final String timeZoneId = config.getString(new String[]{PATTERN, "timeZone"});
+ if (timeZoneId == null) {
+ return JsonTemplateLayoutDefaults.getTimeZone();
+ }
+ boolean found = false;
+ for (final String availableTimeZone : TimeZone.getAvailableIDs()) {
+ if (availableTimeZone.equalsIgnoreCase(timeZoneId)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException(
+ "invalid timestamp time zone: " + config);
+ }
+ return TimeZone.getTimeZone(timeZoneId);
+ }
+
+ }
+
+ private static final class PatternResolver implements EventResolver {
+
+ private final PatternResolverContext patternResolverContext;
+
+ private PatternResolver(final PatternResolverContext patternResolverContext) {
+ this.patternResolverContext = patternResolverContext;
+ }
+
+ @Override
+ public synchronized void resolve(
+ final LogEvent logEvent,
+ final JsonWriter jsonWriter) {
+
+ // Format timestamp if it doesn't match the last cached one.
+ final boolean instantMatching = patternResolverContext.formatter.isInstantMatching(
+ patternResolverContext.lastFormattedInstant,
+ logEvent.getInstant());
+ if (!instantMatching) {
+
+ // Format the timestamp.
+ patternResolverContext.lastFormattedInstantBuffer.setLength(0);
+ patternResolverContext.lastFormattedInstant.initFrom(logEvent.getInstant());
+ patternResolverContext.formatter.format(
+ patternResolverContext.lastFormattedInstant,
+ patternResolverContext.lastFormattedInstantBuffer);
+
+ // Write the formatted timestamp.
+ final StringBuilder jsonWriterStringBuilder = jsonWriter.getStringBuilder();
+ final int startIndex = jsonWriterStringBuilder.length();
+ jsonWriter.writeString(patternResolverContext.lastFormattedInstantBuffer);
+
+ // Cache the written value.
+ patternResolverContext.lastFormattedInstantBuffer.setLength(0);
+ patternResolverContext.lastFormattedInstantBuffer.append(
+ jsonWriterStringBuilder,
+ startIndex,
+ jsonWriterStringBuilder.length());
+
+ }
+
+ // Write the cached formatted timestamp.
+ else {
+ jsonWriter.writeRawString(
+ patternResolverContext.lastFormattedInstantBuffer);
+ }
+
+ }
+
+ }
+}
diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java
new file mode 100644
index 000000000..2022c6d4a
--- /dev/null
+++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java
@@ -0,0 +1,49 @@
+/*
+ * 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.logging.internal;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext;
+import org.apache.logging.log4j.layout.template.json.resolver.EventResolverFactory;
+import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolver;
+import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig;
+import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverFactory;
+
+@Plugin(name = "LambdaTimestampResolverFactory", category = TemplateResolverFactory.CATEGORY)
+public final class LambdaTimestampResolverFactory implements EventResolverFactory {
+
+ private static final LambdaTimestampResolverFactory INSTANCE = new LambdaTimestampResolverFactory();
+
+ private LambdaTimestampResolverFactory() {
+ }
+
+ @PluginFactory
+ public static LambdaTimestampResolverFactory getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public String getName() {
+ return LambdaTimestampResolver.getName();
+ }
+
+ @Override
+ public TemplateResolver create(EventResolverContext context,
+ TemplateResolverConfig config) {
+ return new LambdaTimestampResolver(config);
+ }
+}
diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java
new file mode 100644
index 000000000..e58ca4109
--- /dev/null
+++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java
@@ -0,0 +1,27 @@
+/*
+ * 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.logging.internal;
+
+public class LoggingConstants {
+ public static final String LAMBDA_LOG_LEVEL = System.getenv("AWS_LAMBDA_LOG_LEVEL");
+
+ public static final String LAMBDA_LOG_FORMAT = System.getenv("AWS_LAMBDA_LOG_FORMAT");
+
+ public static final String LOG_DATE_RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+
+ private LoggingConstants() {
+ // constants
+ }
+}
diff --git a/powertools-logging/src/main/resources/LambdaJsonLayout.json b/powertools-logging/src/main/resources/LambdaJsonLayout.json
index dfc1fc78f..da3385032 100644
--- a/powertools-logging/src/main/resources/LambdaJsonLayout.json
+++ b/powertools-logging/src/main/resources/LambdaJsonLayout.json
@@ -1,6 +1,6 @@
{
"timestamp": {
- "$resolver": "timestamp"
+ "$resolver": "lambda-timestamp"
},
"instant": {
"epochSecond": {
diff --git a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java b/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java
index 9b0c6165a..95fb9c47f 100644
--- a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java
+++ b/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java
@@ -16,6 +16,7 @@
import static java.util.Collections.emptyMap;
import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField;
+import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.when;
@@ -32,8 +33,12 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
import java.util.Map;
import org.apache.logging.log4j.Level;
+import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
@@ -42,7 +47,6 @@
import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect;
class LambdaJsonLayoutTest {
-
private RequestHandler