diff --git a/CHANGELOG.md b/CHANGELOG.md index 5619f0bbb..dbb90ce3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) fo ## [Unreleased] +## [1.17.0] - 2023-08-21 + +### Added +* Feat: Add Batch Processor module in (#1317) by @scottgerring +* Feat: Add SNS+SQS large messages module (#1310) by @jeromevdl + +### Maintenance +* fix: use default credentials provider for all provided SDK clients in (#1303) by @roamingthings +* Chore: Make request for Logger explicitly for current class in (#1307) by @jreijn +* Chore: checkstyle formater & linter in (#1316) by @jeromevdl +* Chore: Add powertools specific user-agent-suffix to the AWS SDK v2 clients by @eldimi in (#1306) +* Chore: Add 'v2' branch to build workflows to prepare for v2 work in (#1341) by @scottgerring +* Deps: Bump third party dependencies to the latest versions. + +### Documentation +* Docs: Add maintainers guide in (#1326) by @scottgerring +* Docs: improve contributing guide in (#1334) by @jeromevdl +* Docs: Improve example documentation in (#1291) by @scottgerring +* Docs: Add discord + sec disclosure links to readme in (#1311) by @scottgerring +* Docs: Add external examples from AWS SAM CLI App Templates in (#1318) by @AlexeySoshin +* Docs: Add CDK example in (#1321) by @AlexeySoshin + ## [1.16.1] - 2023-07-19 * Fix: idempotency timeout bug (#1285) by @scottgerring diff --git a/README.md b/README.md index 47fb59bd7..76f8b2e4b 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda powertools-tracing - 1.16.1 + 1.17.0 software.amazon.lambda powertools-logging - 1.16.1 + 1.17.0 software.amazon.lambda powertools-metrics - 1.16.1 + 1.17.0 ... diff --git a/examples/.gitignore b/examples/.gitignore index 79e044a40..892320d3b 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,2 +1,4 @@ dependency-reduced-pom.xml .aws-sam +cdk.out +.m2 diff --git a/examples/README.md b/examples/README.md index 9b76faa82..0744c2bb1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,9 @@ Each example can be copied from its subdirectory and used independently of the r ## Examples -* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules +* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools + * [SAM](./powertools-examples-core/sam) + * [CDK](./powertools-examples-core/cdk) * [powertools-examples-idempotency](powertools-examples-idempotency) - An idempotent HTTP API * [powertools-examples-parameters](powertools-examples-parameters) - Uses the parameters module to provide runtime parameters to a function * [powertools-examples-serialization](powertools-examples-serialization) - Uses the serialization module to serialize and deserialize API Gateway & SQS payloads @@ -34,8 +36,8 @@ amongst other things. To build and deploy an example application for the first time, run the following in your shell: ```bash -# Switch to the directory containing an example for the powertools-core module -$ cd powertools-examples-core +# Switch to the directory containing an example for the powertools-idempotency module +$ cd powertools-examples-idempotency # Build and deploy the example $ sam build @@ -52,6 +54,8 @@ The first command will build the source of your application. The second command You can find your API Gateway Endpoint URL in the output values displayed after deployment. +If you're not using SAM, you can look for examples for other tools under [powertools-examples-core](./powertools-examples-core) + ### External examples You can find more examples in the https://github.com/aws/aws-sam-cli-app-templates project: diff --git a/examples/pom.xml b/examples/pom.xml index aca250dae..f290b3951 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,7 +29,9 @@ - powertools-examples-core + powertools-examples-core/sam + powertools-examples-core/cdk/app + powertools-examples-core/cdk/infra powertools-examples-idempotency powertools-examples-parameters powertools-examples-serialization diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 10e15d532..9efa7fa63 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -15,7 +15,7 @@ 1.8 1.8 true - 2.20.109 + 2.20.128 @@ -37,7 +37,7 @@ com.amazonaws aws-lambda-java-core - 1.2.2 + 1.2.3 software.amazon.awssdk @@ -107,18 +107,16 @@ - - + - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.15 + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.1.0 diff --git a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java index d9339549b..b188df501 100644 --- a/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java +++ b/examples/powertools-examples-batch/src/main/java/org/demo/batch/kinesis/KinesisBatchHandler.java @@ -12,7 +12,7 @@ public class KinesisBatchHandler implements RequestHandler { - private final static Logger LOGGER = LogManager.getLogger(org.demo.batch.sqs.SqsBatchHandler.class); + private final static Logger LOGGER = LogManager.getLogger(KinesisBatchHandler.class); private final BatchMessageHandler handler; public KinesisBatchHandler() { diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c9fe249b3..50cfff85d 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,9 +14,9 @@ 1.8 1.8 true - 1.2.2 + 1.2.3 3.11.2 - 2.20.119 + 2.20.130 @@ -132,18 +132,16 @@ - - + - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.15 + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.1.0 diff --git a/examples/powertools-examples-core/.gitignore b/examples/powertools-examples-core/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index a47d0d26c..f11982477 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -5,17 +5,17 @@ This project demonstrates the Lambda for Powertools Java module - including [tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and [metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). -It is made up of the following: +We provide examples for the following infrastructure-as-code tools: +* [AWS SAM](sam/) +* [AWS CDK](cdk/) -- [App.java](src/main/java/helloworld/App.java) - Code for the application's Lambda function. -- [events](events) - Invocation events that you can use to invoke the function. -- [AppTests.java](src/test/java/helloworld/AppTest.java) - Unit tests for the application code. -- [template.yaml](template.yaml) - A template that defines the application's AWS resources. +For each of the tools, the example application is the same, and consists of the following files: -## Deploy the sample application +- [App.java](sam/src/main/java/helloworld/App.java) - Code for the application's Lambda function. +- [AppTests.java](sam/src/test/java/helloworld/AppTest.java) - Unit tests for the application code. +- [events](sam/events/event.json) - Invocation events that you can use to invoke the function. -This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting -started with SAM in [the examples directory](../README.md) +Configuration files and deployment process for each tool are described in corresponding README files. ## Test the application @@ -35,13 +35,4 @@ different function calls within the example Likewise, from the CloudWatch dashboard, under **Metrics**, **all metrics**, you will find the namespaces `Another` and `ServerlessAirline`. The values in each of these are published by the code in -[App.java](src/main/java/helloworld/App.java). - -You can also watch the trace information or log information using the SAM CLI: -```bash -# Tail the logs -sam logs --tail $MY_STACK - -# Tail the traces -sam traces --tail -``` \ No newline at end of file +[App.java](sam/src/main/java/helloworld/App.java). diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core/cdk/README.md new file mode 100644 index 000000000..f15a24168 --- /dev/null +++ b/examples/powertools-examples-core/cdk/README.md @@ -0,0 +1,36 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with CDK + +This project demonstrates the Lambda for Powertools Java module deployed using [Cloud Development Kit](https://aws.amazon.com/cdk/). + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## Configuration +CDK uses the following project structure: +- [app](./app) - stores the source code of your application, which is similar between all examples +- [infra](./infra) - stores the definition of your infrastructure + - [cdk.json](./infra/cdk.json) - tells the CDK Toolkit how to execute your app + - [CdkApp](./infra/src/main/java/cdk/CdkApp.java) - bootstraps your stack, taking AWS `account` and `region` as input + - [CdkStack](./infra/src/main/java/cdk/CdkStack.java) - defines the Lambda function to be deployed as well as API Gateway for it. + +It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests. + + +## Deploy the sample application + +The minimum to deploy the app should be +```bash +cdk deploy +``` + +If you're running CDK for the first time, you'll need to first run the bootstrap command: +```bash +cdk bootstrap +``` + +## Useful commands + +* `mvn package` compile and run tests +* `cdk synth` emits the synthesized CloudFormation template +* `cdk deploy` deploy this stack to your default AWS account/region +* `cdk diff` compare deployed stack with current state +* `cdk docs` open CDK documentation \ No newline at end of file diff --git a/examples/powertools-examples-core/events/event.json b/examples/powertools-examples-core/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/events/event.json rename to examples/powertools-examples-core/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml new file mode 100644 index 000000000..a1aba1b8d --- /dev/null +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -0,0 +1,200 @@ + + 4.0.0 + + software.amazon.lambda.examples + 1.16.1 + powertools-examples-core-cdk + jar + + Powertools for AWS Lambda (Java) library Examples - Core + + + 2.20.0 + 1.8 + 1.8 + true + + + + + software.amazon.lambda + powertools-tracing + ${project.version} + + + software.amazon.lambda + powertools-logging + ${project.version} + + + software.amazon.lambda + powertools-metrics + ${project.version} + + + com.amazonaws + aws-lambda-java-core + 1.2.3 + + + com.amazonaws + aws-lambda-java-events + 3.11.2 + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + + junit + junit + 4.13.2 + test + + + + + helloworld-lambda + + + dev.aspectj + aspectj-maven-plugin + 1.13.1 + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.target} + + + software.amazon.lambda + powertools-tracing + + + software.amazon.lambda + powertools-logging + + + software.amazon.lambda + powertools-metrics + + + + + + + compile + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + package + + shade + + + + + + + + + + + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.1.0 + + + + + + + + + jdk8 + + (,11) + + + 1.9.7 + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + + + dev.aspectj + aspectj-maven-plugin + ${aspectj.plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.target} + + + software.amazon.lambda + powertools-tracing + + + software.amazon.lambda + powertools-logging + + + software.amazon.lambda + powertools-metrics + + + + + + + compile + test-compile + + + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + + + + diff --git a/examples/powertools-examples-core/src/main/java/helloworld/App.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java similarity index 82% rename from examples/powertools-examples-core/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java index a1ea9a9e3..988da2a73 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java @@ -17,14 +17,11 @@ import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; -import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.amazonaws.xray.AWSXRay; -import com.amazonaws.xray.entities.Entity; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -83,38 +80,17 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv log.info(output); }); - threadOption1(); - - threadOption2(); - log.info("After output"); return response .withStatusCode(200) .withBody(output); - } catch (IOException | InterruptedException e) { + } catch (IOException e) { return response .withBody("{}") .withStatusCode(500); } } - private void threadOption1() throws InterruptedException { - final Entity traceEntity = AWSXRay.getTraceEntity(); - assert traceEntity != null; - traceEntity.run(new Thread(this::log)); - } - - private void threadOption2() throws InterruptedException { - Entity traceEntity = AWSXRay.getTraceEntity(); - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> - { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); - anotherThread.start(); - anotherThread.join(); - } - @Tracing private void log() { log.info("inside threaded logging for function"); diff --git a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/src/main/resources/log4j2.xml b/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/cdk/infra/cdk.json b/examples/powertools-examples-core/cdk/infra/cdk.json new file mode 100644 index 000000000..e24ee7b04 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/cdk.json @@ -0,0 +1,36 @@ +{ + "app": "mvn -e -q compile exec:java", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "target", + "pom.xml", + "src/test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true + } +} diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml new file mode 100644 index 000000000..d81118cde --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + software.amazon.lambda.examples + cdk + 1.18.0-SNAPSHOT + + UTF-8 + 2.91.0 + [10.0.0,11.0.0) + 5.10.0 + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 1.8 + 1.8 + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + cdk.CdkApp + + + + + + + + software.amazon.awscdk + aws-cdk-lib + ${cdk.version} + + + software.constructs + constructs + ${constructs.version} + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java new file mode 100644 index 000000000..d564eb9a0 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java @@ -0,0 +1,53 @@ +/* + * 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 cdk; + +import software.amazon.awscdk.App; +import software.amazon.awscdk.StackProps; + +public class CdkApp { + public static void main(final String[] args) { + App app = new App(); + + new CdkStack(app, "CdkStack", StackProps.builder() + // If you don't specify 'env', this stack will be environment-agnostic. + // Account/Region-dependent features and context lookups will not work, + // but a single synthesized template can be deployed anywhere. + + // Uncomment the next block to specialize this stack for the AWS Account + // and Region that are implied by the current CLI configuration. + /* + .env(Environment.builder() + .account(System.getenv("CDK_DEFAULT_ACCOUNT")) + .region(System.getenv("CDK_DEFAULT_REGION")) + .build()) + */ + + + // Uncomment the next block if you know exactly what Account and Region you + // want to deploy the stack to. + /* + .env(Environment.builder() + .account("1234567890") + .region("region") + .build()) + */ + + // For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html + .build()); + + app.synth(); + } +} diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java new file mode 100644 index 000000000..8e6b44112 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java @@ -0,0 +1,144 @@ +/* + * 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 cdk; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import software.amazon.awscdk.BundlingOptions; +import software.amazon.awscdk.CfnOutput; +import software.amazon.awscdk.Duration; +import software.amazon.awscdk.Stack; +import software.amazon.awscdk.StackProps; +import software.amazon.awscdk.services.apigateway.LambdaIntegration; +import software.amazon.awscdk.services.apigateway.RestApi; +import software.amazon.awscdk.services.lambda.Code; +import software.amazon.awscdk.services.lambda.Function; +import software.amazon.awscdk.services.lambda.Runtime; +import software.amazon.awscdk.services.lambda.Tracing; +import software.amazon.awscdk.services.s3.assets.AssetOptions; +import software.constructs.Construct; + +/** + * Defines a stack that consists of a single Java Lambda function and an API Gateway + */ +public class CdkStack extends Stack { + private static final String SHELL_COMMAND = "/bin/sh"; + private static final String MAVEN_PACKAGE = "mvn package"; + private static final String COPY_OUTPUT = "cp /asset-input/target/helloworld-lambda.jar /asset-output/"; + + public CdkStack(final Construct scope, final String id) { + this(scope, id, null); + } + + public CdkStack(final Construct scope, final String id, final StackProps props) { + super(scope, id, props); + + Function helloWorldFunction = createHelloWorldFunction(); + Function helloWorldStreamFunction = createHelloWorldStreamFunction(); + RestApi restApi = createHelloWorldApi(); + + restApi.getRoot().resourceForPath("/hello") + .addMethod("GET", LambdaIntegration.Builder.create(helloWorldFunction) + .build()); + + restApi.getRoot().resourceForPath("/hellostream") + .addMethod("GET", LambdaIntegration.Builder.create(helloWorldStreamFunction) + .build()); + + outputApiUrl(restApi); + } + + private static List createFunctionPackageInstructions() { + // CDK will use this command to package your Java Lambda + return Arrays.asList( + SHELL_COMMAND, + "-c", + MAVEN_PACKAGE + " && " + + COPY_OUTPUT + ); + } + + /** + * Adds API URL to the outputs + * + * @param restApi + */ + private void outputApiUrl(RestApi restApi) { + CfnOutput.Builder.create(this, "HelloWorldApiUrl") + .description("API Gateway endpoint URL for Prod stage for Hello World function") + .value(restApi.getUrl() + "hello").build(); + } + + // Method to create the Lambda function + private Function createHelloWorldFunction() { + List functionPackageInstructions = createFunctionPackageInstructions(); + + Map environment = new HashMap<>(); + environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); + environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.1"); + environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); + environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); + + return Function.Builder.create(this, "HelloWorldFunction") + .runtime(Runtime.JAVA_11) + .memorySize(512) + .timeout(Duration.seconds(20)) + .tracing(Tracing.ACTIVE) + .code(Code.fromAsset("../app/", AssetOptions.builder() + .bundling(BundlingOptions.builder() + .image(Runtime.JAVA_11.getBundlingImage()) + .command(functionPackageInstructions) + .build()) + .build())) + .handler("helloworld.App") + .environment(environment) + .build(); + } + + private Function createHelloWorldStreamFunction() { + List functionPackageInstructions = createFunctionPackageInstructions(); + + Map environment = new HashMap<>(); + environment.put("POWERTOOLS_LOG_LEVEL", "INFO"); + environment.put("POWERTOOLS_LOGGER_SAMPLE_RATE", "0.7"); + environment.put("POWERTOOLS_LOGGER_LOG_EVENT", "true"); + environment.put("POWERTOOLS_METRICS_NAMESPACE", "Coreutilities"); + environment.put("POWERTOOLS_SERVICE_NAME", "hello"); + + return Function.Builder.create(this, "HelloWorldStreamFunction") + .runtime(Runtime.JAVA_11) + .memorySize(512) + .timeout(Duration.seconds(20)) + .tracing(Tracing.ACTIVE) + .code(Code.fromAsset("../app/", AssetOptions.builder() + .bundling(BundlingOptions.builder() + .image(Runtime.JAVA_11.getBundlingImage()) + .command(functionPackageInstructions) + .build()) + .build())) + .handler("helloworld.AppStream") + .environment(environment) + .build(); + } + + // Method to create the REST API + private RestApi createHelloWorldApi() { + return RestApi.Builder.create(this, "HelloWorldApi") + .description("API Gateway endpoint URL for Prod stage for Hello World function") + .build(); + } +} diff --git a/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java new file mode 100644 index 000000000..29cb15545 --- /dev/null +++ b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java @@ -0,0 +1,48 @@ +/* + * 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 cdk; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import software.amazon.awscdk.App; +import software.amazon.awscdk.assertions.Template; + +public class CdkStackTest { + + @Test + public void testStack() { + App app = new App(); + CdkStack stack = new CdkStack(app, "test"); + + Template template = Template.fromStack(stack); + + // There should be 2 lambda functions, one to handle regular input, and another for streaming + template.resourceCountIs("AWS::Lambda::Function", 2); + + // API Gateway should exist + template.resourceCountIs("AWS::ApiGateway::RestApi", 1); + + // API Gateway should have a path pointing to the regular Lambda + Map resourceProperties = new HashMap<>(); + resourceProperties.put("PathPart", "hello"); + template.hasResourceProperties("AWS::ApiGateway::Resource", resourceProperties); + + // API Gateway should have a path pointing to the streaming Lambda + resourceProperties = new HashMap<>(); + resourceProperties.put("PathPart", "hellostream"); + template.hasResourceProperties("AWS::ApiGateway::Resource", resourceProperties); + } +} diff --git a/examples/powertools-examples-core/sam/README.md b/examples/powertools-examples-core/sam/README.md new file mode 100644 index 000000000..7a4279ae3 --- /dev/null +++ b/examples/powertools-examples-core/sam/README.md @@ -0,0 +1,24 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with SAM + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/). + +For general information on the deployed example itself, you can refer to the parent [README](../README.md) + +## Configuration +SAM uses [template.yaml](template.yaml) to define the application's AWS resources. +This file defines the Lambda function to be deployed as well as API Gateway for it. + +## Deploy the sample application +To deploy the example, check out the instructions for getting +started with SAM in [the examples directory](../../README.md) + +## Additional notes + +You can watch the trace information or log information using the SAM CLI: +```bash +# Tail the logs +sam logs --tail $MY_STACK + +# Tail the traces +sam traces --tail +``` \ No newline at end of file diff --git a/examples/powertools-examples-core/sam/events/event.json b/examples/powertools-examples-core/sam/events/event.json new file mode 100644 index 000000000..3822fadaa --- /dev/null +++ b/examples/powertools-examples-core/sam/events/event.json @@ -0,0 +1,63 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } + } + \ No newline at end of file diff --git a/examples/powertools-examples-core/pom.xml b/examples/powertools-examples-core/sam/pom.xml similarity index 94% rename from examples/powertools-examples-core/pom.xml rename to examples/powertools-examples-core/sam/pom.xml index d402ce09c..bb2e01097 100644 --- a/examples/powertools-examples-core/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -35,7 +35,7 @@ com.amazonaws aws-lambda-java-core - 1.2.2 + 1.2.3 com.amazonaws @@ -106,18 +106,16 @@ - - + - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.15 + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.1.0 diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java new file mode 100644 index 000000000..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java @@ -0,0 +1,107 @@ +/* + * 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 helloworld; + +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.tracing.CaptureMode; +import software.amazon.lambda.powertools.tracing.Tracing; +import software.amazon.lambda.powertools.tracing.TracingUtils; + +/** + * Handler for requests to Lambda function. + */ +public class App implements RequestHandler { + private final static Logger log = LogManager.getLogger(App.class); + + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + Map headers = new HashMap<>(); + + headers.put("Content-Type", "application/json"); + headers.put("X-Custom-Header", "application/json"); + + metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); + + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); + + LoggingUtils.appendKey("test", "willBeLogged"); + + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withHeaders(headers); + try { + final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); + log.info(pageContents); + TracingUtils.putAnnotation("Test", "New"); + String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); + + TracingUtils.withSubsegment("loggingResponse", subsegment -> + { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); + + log.info("After output"); + return response + .withStatusCode(200) + .withBody(output); + } catch (RuntimeException | IOException e) { + return response + .withBody("{}") + .withStatusCode(500); + } + } + + @Tracing + private void log() { + log.info("inside threaded logging for function"); + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + private String getPageContents(String address) throws IOException { + URL url = new URL(address); + putMetadata("getPageContents", address); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + return br.lines().collect(Collectors.joining(System.lineSeparator())); + } + } +} diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.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 helloworld; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; + +public class AppStream implements RequestStreamHandler { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + @Logging(logEvent = true) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + Map map = mapper.readValue(input, Map.class); + + System.out.println(map.size()); + } +} diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml new file mode 100644 index 000000000..e1fd14cea --- /dev/null +++ b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java new file mode 100644 index 000000000..70dad8d71 --- /dev/null +++ b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java @@ -0,0 +1,59 @@ +/* + * 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 helloworld; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.amazonaws.xray.AWSXRay; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class AppTest { + + @Before + public void setup() { + if (null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.beginSegment("test"); + } + } + + @After + public void tearDown() { + if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { + AWSXRay.endSubsegment(); + } + + if (null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.endSegment(); + } + } + + @Test + public void successfulResponse() { + App app = new App(); + APIGatewayProxyResponseEvent result = app.handleRequest(null, null); + assertEquals(result.getStatusCode().intValue(), 200); + assertEquals(result.getHeaders().get("Content-Type"), "application/json"); + String content = result.getBody(); + assertNotNull(content); + assertTrue(content.contains("\"message\"")); + assertTrue(content.contains("\"hello world\"")); + assertTrue(content.contains("\"location\"")); + } +} diff --git a/examples/powertools-examples-core/template.yaml b/examples/powertools-examples-core/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/template.yaml rename to examples/powertools-examples-core/sam/template.yaml diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 38682e164..d0e98ec28 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -48,7 +48,7 @@ com.amazonaws aws-lambda-java-core - 1.2.2 + 1.2.3 com.amazonaws @@ -169,18 +169,16 @@ - - + - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.15 + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.1.0 diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index ac2c7ef1b..72fa621ad 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @@ -113,7 +114,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv */ private String getPageContents(String address) throws IOException { URL url = new URL(address); - try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { return br.lines().collect(Collectors.joining(System.lineSeparator())); } } diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 1c7d177f4..19be585a5 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -27,7 +27,7 @@ com.amazonaws aws-lambda-java-core - 1.2.2 + 1.2.3 com.amazonaws diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 0ec05cd82..d084df983 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -27,7 +27,7 @@ com.amazonaws aws-lambda-java-core - 1.2.2 + 1.2.3 com.amazonaws diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index b3947513f..d4b26d166 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -41,7 +41,7 @@ com.amazonaws aws-lambda-java-core - 1.2.2 + 1.2.3 diff --git a/mkdocs.yml b/mkdocs.yml index 62d8d75ce..cde47c815 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,7 +85,7 @@ extra_javascript: extra: powertools: - version: 1.16.1 # to update after each release (we do not want snapshot version here) + version: 1.17.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 7cdf8a71f..24f71c6bd 100644 --- a/pom.xml +++ b/pom.xml @@ -74,11 +74,11 @@ 2.20.0 2.15.2 1.9.7 - 2.20.119 + 2.20.130 2.14.0 2.1.3 UTF-8 - 1.2.2 + 1.2.3 3.11.2 1.1.2 3.11.0 diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 593b22444..2c878416a 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -9,6 +9,9 @@ 2.0.0-SNAPSHOT + A suite of utilities that makes batch message processing using AWS Lambda easier. + Powertools for AWS Lambda (Java) batch messages + diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 49b4b5a20..7abaec9d0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -120,19 +120,17 @@ - - + - - io.github.edwgiz - log4j-maven-shade-plugin-extensions - 2.17.2 - + + org.apache.logging.log4j + log4j-transform-maven-shade-plugin-extensions + 0.1.0 + diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index e8a3273b1..8f3997dbd 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ 1.8 1.8 10.2.69 - 2.89.0 + 2.91.0 true @@ -162,7 +162,7 @@ org.yaml snakeyaml - 2.0 + 2.1 test