From e66095e098a678cc9e61c5a2db02a39cd5380d18 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Mon, 2 Oct 2023 13:39:41 +0100 Subject: [PATCH 01/59] Setting up Kotlin environment. Converting test to Kotlin. --- .../powertools-examples-core/kotlin/README.md | 38 +++ .../kotlin/build.gradle | 47 ++++ .../kotlin/events/event.json | 62 +++++ .../kotlin/gradle/wrapper/.gitignore | 2 + .../kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../powertools-examples-core/kotlin/gradlew | 248 ++++++++++++++++++ .../kotlin/gradlew.bat | 92 +++++++ .../kotlin/src/main/java/helloworld/App.java | 107 ++++++++ .../src/main/java/helloworld/AppStream.java | 38 +++ .../src/test/kotlin/helloworld/AppTest.kt | 19 ++ .../kotlin/template.yaml | 71 +++++ 12 files changed, 731 insertions(+) create mode 100644 examples/powertools-examples-core/kotlin/README.md create mode 100644 examples/powertools-examples-core/kotlin/build.gradle create mode 100644 examples/powertools-examples-core/kotlin/events/event.json create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/powertools-examples-core/kotlin/gradlew create mode 100644 examples/powertools-examples-core/kotlin/gradlew.bat create mode 100644 examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt create mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md new file mode 100644 index 000000000..c7c798d5d --- /dev/null +++ b/examples/powertools-examples-core/kotlin/README.md @@ -0,0 +1,38 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with Gradle + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with +[Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle +configuration guide [in the main project README](../../../README.md). + +You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, +and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. + + +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. + +The build of the project is managed by Gradle, and configured in [build.gradle](build.gradle). + +## Deploy the sample application +To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: + +```bash +./gradlew build +``` + +Once this is done 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/kotlin/build.gradle b/examples/powertools-examples-core/kotlin/build.gradle new file mode 100644 index 000000000..885de6f43 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/build.gradle @@ -0,0 +1,47 @@ + +plugins { + id 'java' + id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" + id 'org.jetbrains.kotlin.jvm' version '1.9.10' +} + +wrapper { + gradleVersion = "7.6.1" +} + +compileJava { + sourceCompatibility = "1.8" + targetCompatibility = "1.8" + + ajc { + enabled = true + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.amazonaws:aws-lambda-java-core:1.2.2' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' + 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' + testImplementation 'junit:junit:4.13.2' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" +} +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + diff --git a/examples/powertools-examples-core/kotlin/events/event.json b/examples/powertools-examples-core/kotlin/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/events/event.json @@ -0,0 +1,62 @@ +{ + "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" + } +} diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore new file mode 100644 index 000000000..59c09e205 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore @@ -0,0 +1,2 @@ +!gradle-wrapper.jar + diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340 GIT binary patch literal 63375 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≫L+crb^Bz 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/powertools-examples-core/kotlin/gradlew.bat b/examples/powertools-examples-core/kotlin/gradlew.bat new file mode 100644 index 000000000..6689b85be --- /dev/null +++ b/examples/powertools-examples-core/kotlin/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java b/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java new file mode 100644 index 000000000..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core/kotlin/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/kotlin/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/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/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt new file mode 100644 index 000000000..b137aecce --- /dev/null +++ b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt @@ -0,0 +1,19 @@ +package helloworld + +import org.junit.Assert +import org.junit.Test + +class AppTest { + @Test + fun successfulResponse() { + val app = App() + val result = app.handleRequest(null, null) + Assert.assertEquals(200, result.statusCode.toLong()) + Assert.assertEquals("application/json", result.headers["Content-Type"]) + val content = result.body + Assert.assertNotNull(content) + Assert.assertTrue(""""message"""" in content) + Assert.assertTrue(""""hello world"""" in content) + Assert.assertTrue(""""location"""" in content) + } +} diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml new file mode 100644 index 000000000..a717c2998 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/template.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + gradle + + Sample SAM Template for gradle + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: java8 + MemorySize: 512 + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.AppStream::handleRequest + Runtime: java8 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn From ea0b496d79a89e1fe2119236fb1093a93cb85e12 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Mon, 2 Oct 2023 14:54:10 +0100 Subject: [PATCH 02/59] Deploying via SAM successfully. --- .../kotlin/src/main/java/helloworld/App.java | 107 ------------------ .../kotlin/src/main/kotlin/helloworld/App.kt | 100 ++++++++++++++++ .../helloworld/AppStream.kt} | 35 +++--- .../src/test/kotlin/helloworld/AppTest.kt | 1 + 4 files changed, 118 insertions(+), 125 deletions(-) delete mode 100644 examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt rename examples/powertools-examples-core/kotlin/src/main/{java/helloworld/AppStream.java => kotlin/helloworld/AppStream.kt} (53%) diff --git a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java b/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java deleted file mode 100644 index fccc63b9a..000000000 --- a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt new file mode 100644 index 000000000..1084b5791 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt @@ -0,0 +1,100 @@ +/* + * 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.RequestHandler +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent +import com.amazonaws.xray.entities.Subsegment +import org.apache.logging.log4j.LogManager +import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger +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.metrics.MetricsUtils +import software.amazon.lambda.powertools.tracing.CaptureMode +import software.amazon.lambda.powertools.tracing.Tracing +import software.amazon.lambda.powertools.tracing.TracingUtils +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.URL +import java.util.stream.Collectors + +/** + * Handler for requests to Lambda function. + */ + +// TODO: Update context so not optional - this was changed to satisfy test +class App : RequestHandler { + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { // changed context to be optional so can pass null from test + val headers: MutableMap = HashMap() + headers["Content-Type"] = "application/json" + headers["X-Custom-Header"] = "application/json" + MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) + MetricsUtils.withSingleMetric("CustomMetrics2", 1.0, Unit.COUNT, "Another") { metric: MetricsLogger -> + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")) + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")) + } + LoggingUtils.appendKey("test", "willBeLogged") + val response = APIGatewayProxyResponseEvent() + .withHeaders(headers) + return try { + val pageContents = getPageContents("https://checkip.amazonaws.com") + log.info(pageContents) + TracingUtils.putAnnotation("Test", "New") + val output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents) + TracingUtils.withSubsegment("loggingResponse") { subsegment: Subsegment? -> + val sampled = "log something out" + log.info(sampled) + log.info(output) + } + log.info("After output") + response + .withStatusCode(200) + .withBody(output) + } catch (e: RuntimeException) { + response + .withBody("{}") + .withStatusCode(500) + } catch (e: IOException) { + response + .withBody("{}") + .withStatusCode(500) + } + } + + @Tracing + private fun log() { + log.info("inside threaded logging for function") + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + @Throws(IOException::class) + private fun getPageContents(address: String): String { + val url = URL(address) + TracingUtils.putMetadata("getPageContents", address) + BufferedReader(InputStreamReader(url.openStream())).use { br -> return br.lines().collect(Collectors.joining(System.lineSeparator())) } + } + + companion object { + private val log = LogManager.getLogger(App::class.java) + } +} diff --git a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt similarity index 53% rename from examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt index 401ef8c48..99f6bbfa0 100644 --- a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt @@ -11,28 +11,27 @@ * limitations under the License. * */ +package helloworld -package helloworld; +import com.amazonaws.services.lambda.runtime.Context +import com.amazonaws.services.lambda.runtime.RequestStreamHandler +import com.fasterxml.jackson.databind.ObjectMapper +import software.amazon.lambda.powertools.logging.Logging +import software.amazon.lambda.powertools.metrics.Metrics +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream -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 +class AppStream : RequestStreamHandler { @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); + @Throws(IOException::class) + override fun handleRequest(input: InputStream, output: OutputStream, context: Context) { + val map: Map<*, *> = mapper.readValue(input, MutableMap::class.java) + println(map.size) + } - System.out.println(map.size()); + companion object { + private val mapper = ObjectMapper() } } diff --git a/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt index b137aecce..8aae081e8 100644 --- a/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt +++ b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt @@ -1,5 +1,6 @@ package helloworld + import org.junit.Assert import org.junit.Test From 615a0952b056a5053d93e41e77bbc3592ddc1a0d Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Tue, 3 Oct 2023 16:46:04 +0100 Subject: [PATCH 03/59] Added Kotlin example. --- examples/powertools-examples-core/README.md | 10 +++++++ .../powertools-examples-core/kotlin/README.md | 6 ++-- .../kotlin/{build.gradle => _build.gradle} | 0 .../kotlin/build.gradle.kts | 28 +++++++++++++++++++ .../kotlin/src/main/kotlin/helloworld/App.kt | 26 ++++++++--------- 5 files changed, 53 insertions(+), 17 deletions(-) rename examples/powertools-examples-core/kotlin/{build.gradle => _build.gradle} (100%) create mode 100644 examples/powertools-examples-core/kotlin/build.gradle.kts diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 1d1dd031f..f2af999b9 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -15,6 +15,7 @@ We also provide an example showing the integration of SAM, Powertools, and Gradl * [AWS SAM with a Gradle build](gradle/) +## Java For each of the tools, the example application is the same, and consists of the following files: - [App.java](sam/src/main/java/helloworld/App.java) - Code for the application's Lambda function. @@ -23,6 +24,15 @@ For each of the tools, the example application is the same, and consists of the Configuration files and deployment process for each tool are described in corresponding README files. +## Kotlin +For each of the tools, the example application is the same, and consists of the following files: + +- [App.kt](kotlin/src/main/kotlin/helloworld/App.kt) - Code for the application's Lambda function. +- [AppTests.kt](kotlin/src/test/kotlin/helloworld/AppTest.kt) - Unit tests for the application code. +- [events](kotlin/events/event.json) - Invocation events that you can use to invoke the function. + +Configuration files and deployment process for each tool are described in corresponding README files. + ## Test the application Once the app is deployed, you can invoke the endpoint like this: diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md index c7c798d5d..6f0510c48 100644 --- a/examples/powertools-examples-core/kotlin/README.md +++ b/examples/powertools-examples-core/kotlin/README.md @@ -1,6 +1,6 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example with Gradle +# Powertools for AWS Lambda (Kotlin) - Core Utilities Example -This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with +This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle configuration guide [in the main project README](../../../README.md). @@ -14,7 +14,7 @@ For general information on the deployed example itself, you can refer to the par 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. -The build of the project is managed by Gradle, and configured in [build.gradle](build.gradle). +The build of the project is managed by Gradle, and configured in [build.gradle](_build.gradle). ## Deploy the sample application To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: diff --git a/examples/powertools-examples-core/kotlin/build.gradle b/examples/powertools-examples-core/kotlin/_build.gradle similarity index 100% rename from examples/powertools-examples-core/kotlin/build.gradle rename to examples/powertools-examples-core/kotlin/_build.gradle diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts new file mode 100644 index 000000000..a1a9e29da --- /dev/null +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + java + id("io.freefair.aspectj.post-compile-weaving") version "6.6.3" + kotlin("jvm") version "1.9.10" +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.amazonaws:aws-lambda-java-core:1.2.2") + implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.2") + 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") + testImplementation("junit:junit:4.13.2") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") +} + +tasks.compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt index 1084b5791..5b6a45ce8 100644 --- a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt @@ -39,12 +39,12 @@ import java.util.stream.Collectors * Handler for requests to Lambda function. */ -// TODO: Update context so not optional - this was changed to satisfy test class App : RequestHandler { @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { // changed context to be optional so can pass null from test + + override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { // changed context to be optional so can pass null val headers: MutableMap = HashMap() headers["Content-Type"] = "application/json" headers["X-Custom-Header"] = "application/json" @@ -54,30 +54,28 @@ class App : RequestHandler val sampled = "log something out" log.info(sampled) log.info(output) } log.info("After output") - response - .withStatusCode(200) - .withBody(output) + response.withStatusCode(200).withBody(output) } catch (e: RuntimeException) { - response - .withBody("{}") - .withStatusCode(500) + response.withBody("{}").withStatusCode(500) } catch (e: IOException) { - response - .withBody("{}") - .withStatusCode(500) + response.withBody("{}").withStatusCode(500) } } From 51991056697424e728d285c8ca9fa99857c50667 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Tue, 3 Oct 2023 16:55:55 +0100 Subject: [PATCH 04/59] Removing unused Gradle build file. --- .../kotlin/_build.gradle | 47 ------------------- .../kotlin/build.gradle.kts | 1 - 2 files changed, 48 deletions(-) delete mode 100644 examples/powertools-examples-core/kotlin/_build.gradle diff --git a/examples/powertools-examples-core/kotlin/_build.gradle b/examples/powertools-examples-core/kotlin/_build.gradle deleted file mode 100644 index 885de6f43..000000000 --- a/examples/powertools-examples-core/kotlin/_build.gradle +++ /dev/null @@ -1,47 +0,0 @@ - -plugins { - id 'java' - id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" - id 'org.jetbrains.kotlin.jvm' version '1.9.10' -} - -wrapper { - gradleVersion = "7.6.1" -} - -compileJava { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - - ajc { - enabled = true - } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'com.amazonaws:aws-lambda-java-core:1.2.2' - implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' - 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' - testImplementation 'junit:junit:4.13.2' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" -} -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} - diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index a1a9e29da..81237f07a 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - java id("io.freefair.aspectj.post-compile-weaving") version "6.6.3" kotlin("jvm") version "1.9.10" } From 6386358e444b0b868a92b71e0758e6417417e60f Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 13:54:01 +0100 Subject: [PATCH 05/59] Adding SAM template so can be used as an existing project and Java target compatibility --- .../kotlin/build.gradle.kts | 4 ++ .../kotlin/template.yaml | 71 ------------------- 2 files changed, 4 insertions(+), 71 deletions(-) delete mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index 81237f07a..b89b93526 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -25,3 +25,7 @@ tasks.compileKotlin { jvmTarget = "1.8" } } + +java { + targetCompatibility = JavaVersion.VERSION_1_8 +} diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml deleted file mode 100644 index a717c2998..000000000 --- a/examples/powertools-examples-core/kotlin/template.yaml +++ /dev/null @@ -1,71 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - gradle - - Sample SAM Template for gradle - -# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst -Globals: - Function: - Timeout: 20 - Runtime: java11 - MemorySize: 512 - Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html - Environment: - Variables: - # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables - POWERTOOLS_LOG_LEVEL: INFO - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 - POWERTOOLS_LOGGER_LOG_EVENT: true - POWERTOOLS_METRICS_NAMESPACE: Coreutilities - -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction - Properties: - CodeUri: . - Handler: helloworld.App::handleRequest - Runtime: java8 - MemorySize: 512 - Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object - Variables: - POWERTOOLS_SERVICE_NAME: hello - Events: - HelloWorld: - Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api - Properties: - Path: /hello - Method: get - - HelloWorldStreamFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: helloworld.AppStream::handleRequest - Runtime: java8 - MemorySize: 512 - Tracing: Active - Environment: - Variables: - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 - Events: - HelloWorld: - Type: Api - Properties: - Path: /hellostream - Method: get - -Outputs: - # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function - # Find out more about other implicit resources you can reference within SAM - # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api - HelloWorldApi: - Description: "API Gateway endpoint URL for Prod stage for Hello World function" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" - HelloWorldFunction: - Description: "Hello World Lambda Function ARN" - Value: !GetAtt HelloWorldFunction.Arn - HelloWorldFunctionIamRole: - Description: "Implicit IAM Role created for Hello World function" - Value: !GetAtt HelloWorldFunctionRole.Arn From 19679dacaa2d6eae8ebdfb6a6baaae43b312c624 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 13:57:03 +0100 Subject: [PATCH 06/59] Adding SAM template so can be used as an existing project --- .../kotlin/template.yaml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml new file mode 100644 index 000000000..1a1572fca --- /dev/null +++ b/examples/powertools-examples-core/kotlin/template.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + gradle + + Sample SAM Template for gradle + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: java11 + MemorySize: 512 + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.AppStream::handleRequest + Runtime: java11 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn From 17c3a5e1fb4bb139d2304dd6a6ecd4b9472efbc5 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 15:19:15 +0100 Subject: [PATCH 07/59] Updating guidance to use SAM for build and deploy --- examples/powertools-examples-core/kotlin/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md index 6f0510c48..ac1138f4d 100644 --- a/examples/powertools-examples-core/kotlin/README.md +++ b/examples/powertools-examples-core/kotlin/README.md @@ -7,7 +7,6 @@ configuration guide [in the main project README](../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. - For general information on the deployed example itself, you can refer to the parent [README](../README.md) ## Configuration @@ -17,10 +16,10 @@ This file defines the Lambda function to be deployed as well as API Gateway for The build of the project is managed by Gradle, and configured in [build.gradle](_build.gradle). ## Deploy the sample application -To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: +To get started, you can use the included template with SAM to run the build and deploy to your AWS environment: ```bash -./gradlew build +sam build && sam deploy --guided ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in From 1312c0274d853c229d638761a41723335c8e7587 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 15:29:52 +0100 Subject: [PATCH 08/59] Restructuring separate Java and Kotlin examples. --- examples/README.md | 2 +- .../{ => java}/cdk/README.md | 8 ++++---- .../{ => java}/cdk/app/events/event.json | 0 .../{ => java}/cdk/app/pom.xml | 0 .../cdk/app/src/main/java/helloworld/App.java | 0 .../cdk/app/src/main/java/helloworld/AppStream.java | 0 .../cdk/app/src/main/resources/log4j2.xml | 0 .../cdk/app/src/test/java/helloworld/AppTest.java | 0 .../{ => java}/cdk/infra/cdk.json | 0 .../{ => java}/cdk/infra/pom.xml | 0 .../cdk/infra/src/main/java/cdk/CdkApp.java | 0 .../cdk/infra/src/main/java/cdk/CdkStack.java | 0 .../cdk/infra/src/test/java/cdk/CdkStackTest.java | 0 .../{ => java}/gradle/README.md | 6 +++--- .../{ => java}/gradle/build.gradle | 0 .../{ => java}/gradle/events/event.json | 0 .../{ => java}/gradle/gradle/wrapper/.gitignore | 0 .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/gradle/wrapper/gradle-wrapper.properties | 0 .../{ => java}/gradle/gradlew | 0 .../{ => java}/gradle/gradlew.bat | 0 .../gradle/src/main/java/helloworld/App.java | 0 .../gradle/src/main/java/helloworld/AppStream.java | 0 .../gradle/src/test/java/helloworld/AppTest.java | 0 .../{ => java}/gradle/template.yaml | 0 .../{ => java}/sam/README.md | 4 ++-- .../{ => java}/sam/events/event.json | 0 .../powertools-examples-core/{ => java}/sam/pom.xml | 0 .../sam/src/main/java/helloworld/App.java | 0 .../sam/src/main/java/helloworld/AppStream.java | 0 .../{ => java}/sam/src/main/resources/log4j2.xml | 0 .../sam/src/test/java/helloworld/AppTest.java | 0 .../{ => java}/sam/template.yaml | 0 .../{ => java}/serverless/README.md | 4 ++-- .../{ => java}/serverless/pom.xml | 0 .../{ => java}/serverless/serverless.yml | 0 .../serverless/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../serverless/src/main/resources/log4j2.xml | 0 .../src/test/java/helloworld/AppTest.java | 0 .../kotlin/{ => gradle}/README.md | 6 +++--- .../kotlin/{ => gradle}/build.gradle.kts | 0 .../kotlin/{ => gradle}/events/event.json | 0 .../kotlin/gradle/{ => gradle}/wrapper/.gitignore | 0 .../gradle/{ => gradle}/wrapper/gradle-wrapper.jar | Bin .../{ => gradle}/wrapper/gradle-wrapper.properties | 0 .../kotlin/{ => gradle}/gradlew | 0 .../kotlin/{ => gradle}/gradlew.bat | 0 .../{ => gradle}/src/main/kotlin/helloworld/App.kt | 0 .../src/main/kotlin/helloworld/AppStream.kt | 0 .../src/test/kotlin/helloworld/AppTest.kt | 0 .../kotlin/{ => gradle}/template.yaml | 0 52 files changed, 15 insertions(+), 15 deletions(-) rename examples/powertools-examples-core/{ => java}/cdk/README.md (81%) rename examples/powertools-examples-core/{ => java}/cdk/app/events/event.json (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/cdk.json (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/src/main/java/cdk/CdkApp.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/src/main/java/cdk/CdkStack.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/src/test/java/cdk/CdkStackTest.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/README.md (89%) rename examples/powertools-examples-core/{ => java}/gradle/build.gradle (100%) rename examples/powertools-examples-core/{ => java}/gradle/events/event.json (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradle/wrapper/.gitignore (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradlew (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradlew.bat (100%) rename examples/powertools-examples-core/{ => java}/gradle/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/template.yaml (100%) rename examples/powertools-examples-core/{ => java}/sam/README.md (87%) rename examples/powertools-examples-core/{ => java}/sam/events/event.json (100%) rename examples/powertools-examples-core/{ => java}/sam/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/sam/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/sam/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/sam/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => java}/sam/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{ => java}/sam/template.yaml (100%) rename examples/powertools-examples-core/{ => java}/serverless/README.md (86%) rename examples/powertools-examples-core/{ => java}/serverless/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/serverless/serverless.yml (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/README.md (89%) rename examples/powertools-examples-core/kotlin/{ => gradle}/build.gradle.kts (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/events/event.json (100%) rename examples/powertools-examples-core/kotlin/gradle/{ => gradle}/wrapper/.gitignore (100%) rename examples/powertools-examples-core/kotlin/gradle/{ => gradle}/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/kotlin/gradle/{ => gradle}/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/gradlew (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/gradlew.bat (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/src/main/kotlin/helloworld/App.kt (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/src/main/kotlin/helloworld/AppStream.kt (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/src/test/kotlin/helloworld/AppTest.kt (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/template.yaml (100%) diff --git a/examples/README.md b/examples/README.md index 52dc0c1e9..73dc7a1bf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,7 +7,7 @@ Each example can be copied from its subdirectory and used independently of the r * [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) + * [CDK](powertools-examples-core/java/cdk) * [Serverless](./powertools-examples-core/serverless) * [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 diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core/java/cdk/README.md similarity index 81% rename from examples/powertools-examples-core/cdk/README.md rename to examples/powertools-examples-core/java/cdk/README.md index f15a24168..f53936b40 100644 --- a/examples/powertools-examples-core/cdk/README.md +++ b/examples/powertools-examples-core/java/cdk/README.md @@ -2,13 +2,13 @@ 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) +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 +- [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. diff --git a/examples/powertools-examples-core/cdk/app/events/event.json b/examples/powertools-examples-core/java/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/cdk/app/events/event.json rename to examples/powertools-examples-core/java/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/java/cdk/app/pom.xml similarity index 100% rename from examples/powertools-examples-core/cdk/app/pom.xml rename to examples/powertools-examples-core/java/cdk/app/pom.xml diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml b/examples/powertools-examples-core/java/cdk/app/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/java/cdk/app/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/cdk/app/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/cdk/app/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/cdk/infra/cdk.json b/examples/powertools-examples-core/java/cdk/infra/cdk.json similarity index 100% rename from examples/powertools-examples-core/cdk/infra/cdk.json rename to examples/powertools-examples-core/java/cdk/infra/cdk.json diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/java/cdk/infra/pom.xml similarity index 100% rename from examples/powertools-examples-core/cdk/infra/pom.xml rename to examples/powertools-examples-core/java/cdk/infra/pom.xml diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java rename to examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java rename to examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java diff --git a/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java rename to examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java diff --git a/examples/powertools-examples-core/gradle/README.md b/examples/powertools-examples-core/java/gradle/README.md similarity index 89% rename from examples/powertools-examples-core/gradle/README.md rename to examples/powertools-examples-core/java/gradle/README.md index c7c798d5d..a13e8fd3d 100644 --- a/examples/powertools-examples-core/gradle/README.md +++ b/examples/powertools-examples-core/java/gradle/README.md @@ -2,13 +2,13 @@ This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../README.md). +configuration guide [in the main project README](../../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../README.md) +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. @@ -24,7 +24,7 @@ To get started, you can use the Gradle wrapper to bootstrap Gradle and run the b ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../README.md) +[the examples directory](../../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/gradle/build.gradle b/examples/powertools-examples-core/java/gradle/build.gradle similarity index 100% rename from examples/powertools-examples-core/gradle/build.gradle rename to examples/powertools-examples-core/java/gradle/build.gradle diff --git a/examples/powertools-examples-core/gradle/events/event.json b/examples/powertools-examples-core/java/gradle/events/event.json similarity index 100% rename from examples/powertools-examples-core/gradle/events/event.json rename to examples/powertools-examples-core/java/gradle/events/event.json diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/gradle/gradlew b/examples/powertools-examples-core/java/gradle/gradlew similarity index 100% rename from examples/powertools-examples-core/gradle/gradlew rename to examples/powertools-examples-core/java/gradle/gradlew diff --git a/examples/powertools-examples-core/gradle/gradlew.bat b/examples/powertools-examples-core/java/gradle/gradlew.bat similarity index 100% rename from examples/powertools-examples-core/gradle/gradlew.bat rename to examples/powertools-examples-core/java/gradle/gradlew.bat diff --git a/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/gradle/template.yaml b/examples/powertools-examples-core/java/gradle/template.yaml similarity index 100% rename from examples/powertools-examples-core/gradle/template.yaml rename to examples/powertools-examples-core/java/gradle/template.yaml diff --git a/examples/powertools-examples-core/sam/README.md b/examples/powertools-examples-core/java/sam/README.md similarity index 87% rename from examples/powertools-examples-core/sam/README.md rename to examples/powertools-examples-core/java/sam/README.md index 7a4279ae3..3b8c31ebc 100644 --- a/examples/powertools-examples-core/sam/README.md +++ b/examples/powertools-examples-core/java/sam/README.md @@ -2,7 +2,7 @@ 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) +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. @@ -10,7 +10,7 @@ This file defines the Lambda function to be deployed as well as API Gateway for ## Deploy the sample application To deploy the example, check out the instructions for getting -started with SAM in [the examples directory](../../README.md) +started with SAM in [the examples directory](../../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/sam/events/event.json b/examples/powertools-examples-core/java/sam/events/event.json similarity index 100% rename from examples/powertools-examples-core/sam/events/event.json rename to examples/powertools-examples-core/java/sam/events/event.json diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/java/sam/pom.xml similarity index 100% rename from examples/powertools-examples-core/sam/pom.xml rename to examples/powertools-examples-core/java/sam/pom.xml diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/sam/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/sam/template.yaml b/examples/powertools-examples-core/java/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/sam/template.yaml rename to examples/powertools-examples-core/java/sam/template.yaml diff --git a/examples/powertools-examples-core/serverless/README.md b/examples/powertools-examples-core/java/serverless/README.md similarity index 86% rename from examples/powertools-examples-core/serverless/README.md rename to examples/powertools-examples-core/java/serverless/README.md index aec093182..d97e7465a 100644 --- a/examples/powertools-examples-core/serverless/README.md +++ b/examples/powertools-examples-core/java/serverless/README.md @@ -1,11 +1,11 @@ # Powertools for AWS Lambda (Java) - Core Utilities Example with Serverless Framework This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Framework](https://www.serverless.com/framework). -For general information on the deployed example itself, you can refer to the parent [README](../README.md). +For general information on the deployed example itself, you can refer to the parent [README](../../README.md). To install Serverless Framework if you don't have it yet, you can follow the [Getting Started Guide](https://www.serverless.com/framework/docs/getting-started). ## Configuration -Serverless Framework uses [serverless.yml](./serverless.yml) to define the application's AWS resources. +Serverless Framework uses [serverless.yml](serverless.yml) to define the application's AWS resources. This file 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. diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/java/serverless/pom.xml similarity index 100% rename from examples/powertools-examples-core/serverless/pom.xml rename to examples/powertools-examples-core/java/serverless/pom.xml diff --git a/examples/powertools-examples-core/serverless/serverless.yml b/examples/powertools-examples-core/java/serverless/serverless.yml similarity index 100% rename from examples/powertools-examples-core/serverless/serverless.yml rename to examples/powertools-examples-core/java/serverless/serverless.yml diff --git a/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/gradle/README.md similarity index 89% rename from examples/powertools-examples-core/kotlin/README.md rename to examples/powertools-examples-core/kotlin/gradle/README.md index ac1138f4d..14059f178 100644 --- a/examples/powertools-examples-core/kotlin/README.md +++ b/examples/powertools-examples-core/kotlin/gradle/README.md @@ -2,12 +2,12 @@ This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../README.md). +configuration guide [in the main project README](../../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../README.md) +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. @@ -23,7 +23,7 @@ sam build && sam deploy --guided ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../README.md) +[the examples directory](../../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts similarity index 100% rename from examples/powertools-examples-core/kotlin/build.gradle.kts rename to examples/powertools-examples-core/kotlin/gradle/build.gradle.kts diff --git a/examples/powertools-examples-core/kotlin/events/event.json b/examples/powertools-examples-core/kotlin/gradle/events/event.json similarity index 100% rename from examples/powertools-examples-core/kotlin/events/event.json rename to examples/powertools-examples-core/kotlin/gradle/events/event.json diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/kotlin/gradlew b/examples/powertools-examples-core/kotlin/gradle/gradlew similarity index 100% rename from examples/powertools-examples-core/kotlin/gradlew rename to examples/powertools-examples-core/kotlin/gradle/gradlew diff --git a/examples/powertools-examples-core/kotlin/gradlew.bat b/examples/powertools-examples-core/kotlin/gradle/gradlew.bat similarity index 100% rename from examples/powertools-examples-core/kotlin/gradlew.bat rename to examples/powertools-examples-core/kotlin/gradle/gradlew.bat diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt rename to examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt rename to examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt diff --git a/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt rename to examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/gradle/template.yaml similarity index 100% rename from examples/powertools-examples-core/kotlin/template.yaml rename to examples/powertools-examples-core/kotlin/gradle/template.yaml From 18c7f48c0484aca9387c383013d3019a7dcdf30a Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 15:30:50 +0100 Subject: [PATCH 09/59] Updating core examples readme to represent new structure for Java and Kotlin examples. --- examples/powertools-examples-core/README.md | 41 +++++++++------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index f2af999b9..787b691ab 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,35 +1,29 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example +# Powertools for AWS Lambda - Core Utilities Example -This project demonstrates the Lambda for Powertools Java module - including +This project demonstrates the Lambda for Powertools Java module - including [logging](https://docs.powertools.aws.dev/lambda/java/core/logging/), [tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and [metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). -We provide examples for the following infrastructure-as-code tools: - -* [AWS SAM](sam/) -* [AWS CDK](cdk/) -* [Serverless framework](serverless/) - -We also provide an example showing the integration of SAM, Powertools, and Gradle: - -* [AWS SAM with a Gradle build](gradle/) - ## Java -For each of the tools, the example application is the same, and consists of the following files: +* [CDK](java/cdk/) +* [Gradle](java/gradle/) +* [SAM](java/sam/) +* [Serverless](java/serverless/) -- [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. +For each of the tools, the example application is the same, and consists of the following files: +- App.java - Code for the application's Lambda function. +- AppTests.java - Unit tests for the application code. +- events - Invocation events that you can use to invoke the function. Configuration files and deployment process for each tool are described in corresponding README files. ## Kotlin -For each of the tools, the example application is the same, and consists of the following files: +* [Gradle](java/gradle/) -- [App.kt](kotlin/src/main/kotlin/helloworld/App.kt) - Code for the application's Lambda function. -- [AppTests.kt](kotlin/src/test/kotlin/helloworld/AppTest.kt) - Unit tests for the application code. -- [events](kotlin/events/event.json) - Invocation events that you can use to invoke the function. +- App.kt - Code for the application's Lambda function. +- AppTests.kt - Unit tests for the application code. +- events - Invocation events that you can use to invoke the function. Configuration files and deployment process for each tool are described in corresponding README files. @@ -41,14 +35,13 @@ Once the app is deployed, you can invoke the endpoint like this: curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ ``` -The response itself isn't particularly interesting - you will get back some information about your IP address. If +The response itself isn't particularly interesting - you will get back some information about your IP address. If you go to the Lambda Console and locate the lambda you have deployed, then click the "Monitoring" tab you will be able to find: * **View X-Ray traces** - Display the traces captured by the traces module. These include subsegments for the -different function calls within the example + different function calls within the example * **View Cloudwatch logs** - Display the structured logging output of 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](sam/src/main/java/helloworld/App.java). +and `ServerlessAirline`. The values in each of these are published by the code in the respective application's Lambda function. From d968f3cce8d566f3e1d587dc5eb6b2bd5f58b11c Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 16:11:12 +0100 Subject: [PATCH 10/59] Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive --- examples/powertools-examples-core/README.md | 5 ++++- .../kotlin/gradle/build.gradle.kts | 4 ++-- .../kotlin/gradle/src/main/kotlin/helloworld/App.kt | 8 +++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 787b691ab..3fa3628b8 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -5,13 +5,15 @@ 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/). +The example application is the same, and you can now also use Kotlin! + ## Java * [CDK](java/cdk/) * [Gradle](java/gradle/) * [SAM](java/sam/) * [Serverless](java/serverless/) -For each of the tools, the example application is the same, and consists of the following files: +Example application consists of the following files: - App.java - Code for the application's Lambda function. - AppTests.java - Unit tests for the application code. - events - Invocation events that you can use to invoke the function. @@ -21,6 +23,7 @@ Configuration files and deployment process for each tool are described in corres ## Kotlin * [Gradle](java/gradle/) +Example application consists of the following files: - App.kt - Code for the application's Lambda function. - AppTests.kt - Unit tests for the application code. - events - Invocation events that you can use to invoke the function. diff --git a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts index b89b93526..a6e831d9a 100644 --- a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts @@ -26,6 +26,6 @@ tasks.compileKotlin { } } -java { - targetCompatibility = JavaVersion.VERSION_1_8 +kotlin { + jvmToolchain(8) } diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt index 5b6a45ce8..796f6f999 100644 --- a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt @@ -45,7 +45,7 @@ class App : RequestHandler = HashMap() + val headers = mutableMapOf() headers["Content-Type"] = "application/json" headers["X-Custom-Header"] = "application/json" MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) @@ -65,7 +65,7 @@ class App : RequestHandler + TracingUtils.withSubsegment("loggingResponse") { _: Subsegment? -> val sampled = "log something out" log.info(sampled) log.info(output) @@ -92,7 +92,5 @@ class App : RequestHandler return br.lines().collect(Collectors.joining(System.lineSeparator())) } } - companion object { - private val log = LogManager.getLogger(App::class.java) - } + private val log = LogManager.getLogger(App::class) } From 5ce71f7c475b746142b14247d0e83d9a1c7c69d0 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 16:34:44 +0100 Subject: [PATCH 11/59] Updating to fix trailing \n --- .../kotlin/gradle/src/main/kotlin/helloworld/App.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt index 796f6f999..0cc8d1098 100644 --- a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt @@ -89,7 +89,9 @@ class App : RequestHandler return br.lines().collect(Collectors.joining(System.lineSeparator())) } + return InputStreamReader(url.openStream()).use { reader -> + reader.readText().trim() + } } private val log = LogManager.getLogger(App::class) From 35fbbaceb1acba3832d4b0ec73377bf524e22b61 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 16:45:49 +0100 Subject: [PATCH 12/59] Updating guidance to be more specific for examples --- examples/README.md | 12 ++++++++---- .../powertools-examples-core/kotlin/gradle/README.md | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/README.md b/examples/README.md index 73dc7a1bf..7d779f8f1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,10 +5,14 @@ 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 with different build tools - * [SAM](./powertools-examples-core/sam) - * [CDK](powertools-examples-core/java/cdk) - * [Serverless](./powertools-examples-core/serverless) +* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools and languages + * [Java](./powertools-examples-core/java) + * [CDK](./powertools-examples-core/java/cdk) + * [Gradle](./powertools-examples-core/java/gradle) + * [SAM](./powertools-examples-core/java/sam) + * [Serverless](./powertools-examples-core/java/serverless) + * [Kotlin](./powertools-examples-core/kotlin) + * [Gradle](./powertools-examples-core/kotlin/gradle) * [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 diff --git a/examples/powertools-examples-core/kotlin/gradle/README.md b/examples/powertools-examples-core/kotlin/gradle/README.md index 14059f178..d40397a4b 100644 --- a/examples/powertools-examples-core/kotlin/gradle/README.md +++ b/examples/powertools-examples-core/kotlin/gradle/README.md @@ -13,7 +13,8 @@ For general information on the deployed example itself, you can refer to the par 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. -The build of the project is managed by Gradle, and configured in [build.gradle](_build.gradle). +The build of the project is managed by Gradle, and configured in [build.gradle.kts](build.gradle.kts) +. ## Deploy the sample application To get started, you can use the included template with SAM to run the build and deploy to your AWS environment: From d277c5c4e8ce23a3b3f0e1bb0d317317a1817b33 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 6 Oct 2023 13:17:01 +0100 Subject: [PATCH 13/59] Adopting new mechanism for specifying jvm target. --- .../kotlin/gradle/build.gradle.kts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts index a6e831d9a..68ff9b6d4 100644 --- a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts @@ -26,6 +26,12 @@ tasks.compileKotlin { } } -kotlin { - jvmToolchain(8) +tasks.compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } } +// If using JDK 11 or higher, use the following instead: +//kotlin { +// jvmToolchain(8) +//} \ No newline at end of file From 6c4646e079d23e680bfad8b4a0eacf96a5dcadcd Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 6 Oct 2023 13:35:59 +0100 Subject: [PATCH 14/59] accommodating new project structure --- examples/README.md | 2 +- examples/pom.xml | 8 ++++---- examples/powertools-examples-core/README.md | 2 +- .../kotlin/{gradle => }/README.md | 6 +++--- .../kotlin/{gradle => }/build.gradle.kts | 1 + .../kotlin/{gradle => }/events/event.json | 0 .../kotlin/gradle/{gradle => }/wrapper/.gitignore | 0 .../gradle/{gradle => }/wrapper/gradle-wrapper.jar | Bin .../{gradle => }/wrapper/gradle-wrapper.properties | 0 .../kotlin/{gradle => }/gradlew | 0 .../kotlin/{gradle => }/gradlew.bat | 0 .../{gradle => }/src/main/kotlin/helloworld/App.kt | 0 .../src/main/kotlin/helloworld/AppStream.kt | 0 .../src/test/kotlin/helloworld/AppTest.kt | 0 .../kotlin/{gradle => }/template.yaml | 0 15 files changed, 10 insertions(+), 9 deletions(-) rename examples/powertools-examples-core/kotlin/{gradle => }/README.md (89%) rename examples/powertools-examples-core/kotlin/{gradle => }/build.gradle.kts (99%) rename examples/powertools-examples-core/kotlin/{gradle => }/events/event.json (100%) rename examples/powertools-examples-core/kotlin/gradle/{gradle => }/wrapper/.gitignore (100%) rename examples/powertools-examples-core/kotlin/gradle/{gradle => }/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/kotlin/gradle/{gradle => }/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/gradlew (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/gradlew.bat (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/src/main/kotlin/helloworld/App.kt (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/src/main/kotlin/helloworld/AppStream.kt (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/src/test/kotlin/helloworld/AppTest.kt (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/template.yaml (100%) diff --git a/examples/README.md b/examples/README.md index 7d779f8f1..cd5c32fa4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,7 +12,7 @@ Each example can be copied from its subdirectory and used independently of the r * [SAM](./powertools-examples-core/java/sam) * [Serverless](./powertools-examples-core/java/serverless) * [Kotlin](./powertools-examples-core/kotlin) - * [Gradle](./powertools-examples-core/kotlin/gradle) + * [Gradle](powertools-examples-core/kotlin/gradle1) * [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 diff --git a/examples/pom.xml b/examples/pom.xml index 810ec1b36..ba7b5ff65 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,10 +29,10 @@ - powertools-examples-core/sam - powertools-examples-core/cdk/app - powertools-examples-core/cdk/infra - powertools-examples-core/serverless + powertools-examples-core/java/sam + powertools-examples-core/java/cdk/app + powertools-examples-core/java/cdk/infra + powertools-examples-core/java/serverless powertools-examples-idempotency powertools-examples-parameters powertools-examples-serialization diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 3fa3628b8..0c9a44a36 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -21,7 +21,7 @@ Example application consists of the following files: Configuration files and deployment process for each tool are described in corresponding README files. ## Kotlin -* [Gradle](java/gradle/) +* [Gradle](kotlin/) Example application consists of the following files: - App.kt - Code for the application's Lambda function. diff --git a/examples/powertools-examples-core/kotlin/gradle/README.md b/examples/powertools-examples-core/kotlin/README.md similarity index 89% rename from examples/powertools-examples-core/kotlin/gradle/README.md rename to examples/powertools-examples-core/kotlin/README.md index d40397a4b..da9ec5b52 100644 --- a/examples/powertools-examples-core/kotlin/gradle/README.md +++ b/examples/powertools-examples-core/kotlin/README.md @@ -2,12 +2,12 @@ This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../../README.md). +configuration guide [in the main project README](../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../../README.md) +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. @@ -24,7 +24,7 @@ sam build && sam deploy --guided ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../../README.md) +[the examples directory](../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts similarity index 99% rename from examples/powertools-examples-core/kotlin/gradle/build.gradle.kts rename to examples/powertools-examples-core/kotlin/build.gradle.kts index 68ff9b6d4..2a11034b6 100644 --- a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -31,6 +31,7 @@ tasks.compileTestKotlin { jvmTarget = "1.8" } } + // If using JDK 11 or higher, use the following instead: //kotlin { // jvmToolchain(8) diff --git a/examples/powertools-examples-core/kotlin/gradle/events/event.json b/examples/powertools-examples-core/kotlin/events/event.json similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/events/event.json rename to examples/powertools-examples-core/kotlin/events/event.json diff --git a/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/kotlin/gradle/gradlew b/examples/powertools-examples-core/kotlin/gradlew similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradlew rename to examples/powertools-examples-core/kotlin/gradlew diff --git a/examples/powertools-examples-core/kotlin/gradle/gradlew.bat b/examples/powertools-examples-core/kotlin/gradlew.bat similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradlew.bat rename to examples/powertools-examples-core/kotlin/gradlew.bat diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt rename to examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt rename to examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt diff --git a/examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt rename to examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt diff --git a/examples/powertools-examples-core/kotlin/gradle/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/template.yaml rename to examples/powertools-examples-core/kotlin/template.yaml From 6eecb90a515b4b6459897616302b000cd36020c3 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 6 Oct 2023 13:41:06 +0100 Subject: [PATCH 15/59] Fixing link typo after refactoring --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index cd5c32fa4..d593e1f06 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,7 +12,7 @@ Each example can be copied from its subdirectory and used independently of the r * [SAM](./powertools-examples-core/java/sam) * [Serverless](./powertools-examples-core/java/serverless) * [Kotlin](./powertools-examples-core/kotlin) - * [Gradle](powertools-examples-core/kotlin/gradle1) + * [Gradle](powertools-examples-core/kotlin/) * [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 From 1431899069e8839cb15406ce436017f14b8d2386 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Mon, 2 Oct 2023 13:39:41 +0100 Subject: [PATCH 16/59] Setting up Kotlin environment. Converting test to Kotlin. --- .../powertools-examples-core/kotlin/README.md | 38 +++ .../kotlin/build.gradle | 47 ++++ .../kotlin/events/event.json | 62 +++++ .../kotlin/gradle/wrapper/.gitignore | 2 + .../kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../powertools-examples-core/kotlin/gradlew | 248 ++++++++++++++++++ .../kotlin/gradlew.bat | 92 +++++++ .../kotlin/src/main/java/helloworld/App.java | 107 ++++++++ .../src/main/java/helloworld/AppStream.java | 38 +++ .../src/test/kotlin/helloworld/AppTest.kt | 19 ++ .../kotlin/template.yaml | 71 +++++ 12 files changed, 731 insertions(+) create mode 100644 examples/powertools-examples-core/kotlin/README.md create mode 100644 examples/powertools-examples-core/kotlin/build.gradle create mode 100644 examples/powertools-examples-core/kotlin/events/event.json create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/powertools-examples-core/kotlin/gradlew create mode 100644 examples/powertools-examples-core/kotlin/gradlew.bat create mode 100644 examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java create mode 100644 examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt create mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md new file mode 100644 index 000000000..c7c798d5d --- /dev/null +++ b/examples/powertools-examples-core/kotlin/README.md @@ -0,0 +1,38 @@ +# Powertools for AWS Lambda (Java) - Core Utilities Example with Gradle + +This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with +[Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle +configuration guide [in the main project README](../../../README.md). + +You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, +and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. + + +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. + +The build of the project is managed by Gradle, and configured in [build.gradle](build.gradle). + +## Deploy the sample application +To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: + +```bash +./gradlew build +``` + +Once this is done 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/kotlin/build.gradle b/examples/powertools-examples-core/kotlin/build.gradle new file mode 100644 index 000000000..885de6f43 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/build.gradle @@ -0,0 +1,47 @@ + +plugins { + id 'java' + id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" + id 'org.jetbrains.kotlin.jvm' version '1.9.10' +} + +wrapper { + gradleVersion = "7.6.1" +} + +compileJava { + sourceCompatibility = "1.8" + targetCompatibility = "1.8" + + ajc { + enabled = true + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.amazonaws:aws-lambda-java-core:1.2.2' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' + 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' + testImplementation 'junit:junit:4.13.2' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" +} +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + diff --git a/examples/powertools-examples-core/kotlin/events/event.json b/examples/powertools-examples-core/kotlin/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/events/event.json @@ -0,0 +1,62 @@ +{ + "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" + } +} diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore new file mode 100644 index 000000000..59c09e205 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore @@ -0,0 +1,2 @@ +!gradle-wrapper.jar + diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340 GIT binary patch literal 63375 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≫L+crb^Bz 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/powertools-examples-core/kotlin/gradlew.bat b/examples/powertools-examples-core/kotlin/gradlew.bat new file mode 100644 index 000000000..6689b85be --- /dev/null +++ b/examples/powertools-examples-core/kotlin/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java b/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java new file mode 100644 index 000000000..fccc63b9a --- /dev/null +++ b/examples/powertools-examples-core/kotlin/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/kotlin/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java new file mode 100644 index 000000000..401ef8c48 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/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/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt new file mode 100644 index 000000000..b137aecce --- /dev/null +++ b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt @@ -0,0 +1,19 @@ +package helloworld + +import org.junit.Assert +import org.junit.Test + +class AppTest { + @Test + fun successfulResponse() { + val app = App() + val result = app.handleRequest(null, null) + Assert.assertEquals(200, result.statusCode.toLong()) + Assert.assertEquals("application/json", result.headers["Content-Type"]) + val content = result.body + Assert.assertNotNull(content) + Assert.assertTrue(""""message"""" in content) + Assert.assertTrue(""""hello world"""" in content) + Assert.assertTrue(""""location"""" in content) + } +} diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml new file mode 100644 index 000000000..a717c2998 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/template.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + gradle + + Sample SAM Template for gradle + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: java8 + MemorySize: 512 + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.AppStream::handleRequest + Runtime: java8 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn From 339676697f010f4a8f7c176dcf4e3bab727fd13e Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Mon, 2 Oct 2023 14:54:10 +0100 Subject: [PATCH 17/59] Deploying via SAM successfully. --- .../kotlin/src/main/java/helloworld/App.java | 107 ------------------ .../kotlin/src/main/kotlin/helloworld/App.kt | 100 ++++++++++++++++ .../helloworld/AppStream.kt} | 35 +++--- .../src/test/kotlin/helloworld/AppTest.kt | 1 + 4 files changed, 118 insertions(+), 125 deletions(-) delete mode 100644 examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java create mode 100644 examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt rename examples/powertools-examples-core/kotlin/src/main/{java/helloworld/AppStream.java => kotlin/helloworld/AppStream.kt} (53%) diff --git a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java b/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java deleted file mode 100644 index fccc63b9a..000000000 --- a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/App.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt new file mode 100644 index 000000000..1084b5791 --- /dev/null +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt @@ -0,0 +1,100 @@ +/* + * 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.RequestHandler +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent +import com.amazonaws.xray.entities.Subsegment +import org.apache.logging.log4j.LogManager +import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger +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.metrics.MetricsUtils +import software.amazon.lambda.powertools.tracing.CaptureMode +import software.amazon.lambda.powertools.tracing.Tracing +import software.amazon.lambda.powertools.tracing.TracingUtils +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.URL +import java.util.stream.Collectors + +/** + * Handler for requests to Lambda function. + */ + +// TODO: Update context so not optional - this was changed to satisfy test +class App : RequestHandler { + @Logging(logEvent = true, samplingRate = 0.7) + @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) + @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { // changed context to be optional so can pass null from test + val headers: MutableMap = HashMap() + headers["Content-Type"] = "application/json" + headers["X-Custom-Header"] = "application/json" + MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) + MetricsUtils.withSingleMetric("CustomMetrics2", 1.0, Unit.COUNT, "Another") { metric: MetricsLogger -> + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")) + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")) + } + LoggingUtils.appendKey("test", "willBeLogged") + val response = APIGatewayProxyResponseEvent() + .withHeaders(headers) + return try { + val pageContents = getPageContents("https://checkip.amazonaws.com") + log.info(pageContents) + TracingUtils.putAnnotation("Test", "New") + val output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents) + TracingUtils.withSubsegment("loggingResponse") { subsegment: Subsegment? -> + val sampled = "log something out" + log.info(sampled) + log.info(output) + } + log.info("After output") + response + .withStatusCode(200) + .withBody(output) + } catch (e: RuntimeException) { + response + .withBody("{}") + .withStatusCode(500) + } catch (e: IOException) { + response + .withBody("{}") + .withStatusCode(500) + } + } + + @Tracing + private fun log() { + log.info("inside threaded logging for function") + } + + @Tracing(namespace = "getPageContents", captureMode = CaptureMode.DISABLED) + @Throws(IOException::class) + private fun getPageContents(address: String): String { + val url = URL(address) + TracingUtils.putMetadata("getPageContents", address) + BufferedReader(InputStreamReader(url.openStream())).use { br -> return br.lines().collect(Collectors.joining(System.lineSeparator())) } + } + + companion object { + private val log = LogManager.getLogger(App::class.java) + } +} diff --git a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt similarity index 53% rename from examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt index 401ef8c48..99f6bbfa0 100644 --- a/examples/powertools-examples-core/kotlin/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt @@ -11,28 +11,27 @@ * limitations under the License. * */ +package helloworld -package helloworld; +import com.amazonaws.services.lambda.runtime.Context +import com.amazonaws.services.lambda.runtime.RequestStreamHandler +import com.fasterxml.jackson.databind.ObjectMapper +import software.amazon.lambda.powertools.logging.Logging +import software.amazon.lambda.powertools.metrics.Metrics +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream -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 +class AppStream : RequestStreamHandler { @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); + @Throws(IOException::class) + override fun handleRequest(input: InputStream, output: OutputStream, context: Context) { + val map: Map<*, *> = mapper.readValue(input, MutableMap::class.java) + println(map.size) + } - System.out.println(map.size()); + companion object { + private val mapper = ObjectMapper() } } diff --git a/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt index b137aecce..8aae081e8 100644 --- a/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt +++ b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt @@ -1,5 +1,6 @@ package helloworld + import org.junit.Assert import org.junit.Test From baa0e3f3974530b56e5f280132d8f716b3c35d62 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Tue, 3 Oct 2023 16:46:04 +0100 Subject: [PATCH 18/59] Added Kotlin example. --- examples/powertools-examples-core/README.md | 10 +++++++ .../powertools-examples-core/kotlin/README.md | 6 ++-- .../kotlin/{build.gradle => _build.gradle} | 0 .../kotlin/build.gradle.kts | 28 +++++++++++++++++++ .../kotlin/src/main/kotlin/helloworld/App.kt | 26 ++++++++--------- 5 files changed, 53 insertions(+), 17 deletions(-) rename examples/powertools-examples-core/kotlin/{build.gradle => _build.gradle} (100%) create mode 100644 examples/powertools-examples-core/kotlin/build.gradle.kts diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 1d1dd031f..f2af999b9 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -15,6 +15,7 @@ We also provide an example showing the integration of SAM, Powertools, and Gradl * [AWS SAM with a Gradle build](gradle/) +## Java For each of the tools, the example application is the same, and consists of the following files: - [App.java](sam/src/main/java/helloworld/App.java) - Code for the application's Lambda function. @@ -23,6 +24,15 @@ For each of the tools, the example application is the same, and consists of the Configuration files and deployment process for each tool are described in corresponding README files. +## Kotlin +For each of the tools, the example application is the same, and consists of the following files: + +- [App.kt](kotlin/src/main/kotlin/helloworld/App.kt) - Code for the application's Lambda function. +- [AppTests.kt](kotlin/src/test/kotlin/helloworld/AppTest.kt) - Unit tests for the application code. +- [events](kotlin/events/event.json) - Invocation events that you can use to invoke the function. + +Configuration files and deployment process for each tool are described in corresponding README files. + ## Test the application Once the app is deployed, you can invoke the endpoint like this: diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md index c7c798d5d..6f0510c48 100644 --- a/examples/powertools-examples-core/kotlin/README.md +++ b/examples/powertools-examples-core/kotlin/README.md @@ -1,6 +1,6 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example with Gradle +# Powertools for AWS Lambda (Kotlin) - Core Utilities Example -This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with +This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle configuration guide [in the main project README](../../../README.md). @@ -14,7 +14,7 @@ For general information on the deployed example itself, you can refer to the par 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. -The build of the project is managed by Gradle, and configured in [build.gradle](build.gradle). +The build of the project is managed by Gradle, and configured in [build.gradle](_build.gradle). ## Deploy the sample application To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: diff --git a/examples/powertools-examples-core/kotlin/build.gradle b/examples/powertools-examples-core/kotlin/_build.gradle similarity index 100% rename from examples/powertools-examples-core/kotlin/build.gradle rename to examples/powertools-examples-core/kotlin/_build.gradle diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts new file mode 100644 index 000000000..a1a9e29da --- /dev/null +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + java + id("io.freefair.aspectj.post-compile-weaving") version "6.6.3" + kotlin("jvm") version "1.9.10" +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.amazonaws:aws-lambda-java-core:1.2.2") + implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.2") + 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") + testImplementation("junit:junit:4.13.2") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") +} + +tasks.compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt index 1084b5791..5b6a45ce8 100644 --- a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt @@ -39,12 +39,12 @@ import java.util.stream.Collectors * Handler for requests to Lambda function. */ -// TODO: Update context so not optional - this was changed to satisfy test class App : RequestHandler { @Logging(logEvent = true, samplingRate = 0.7) @Tracing(captureMode = CaptureMode.RESPONSE_AND_ERROR) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { // changed context to be optional so can pass null from test + + override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { // changed context to be optional so can pass null val headers: MutableMap = HashMap() headers["Content-Type"] = "application/json" headers["X-Custom-Header"] = "application/json" @@ -54,30 +54,28 @@ class App : RequestHandler val sampled = "log something out" log.info(sampled) log.info(output) } log.info("After output") - response - .withStatusCode(200) - .withBody(output) + response.withStatusCode(200).withBody(output) } catch (e: RuntimeException) { - response - .withBody("{}") - .withStatusCode(500) + response.withBody("{}").withStatusCode(500) } catch (e: IOException) { - response - .withBody("{}") - .withStatusCode(500) + response.withBody("{}").withStatusCode(500) } } From 16380a89bb27c72d8dd49a12282d6e16d96d258c Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Tue, 3 Oct 2023 16:55:55 +0100 Subject: [PATCH 19/59] Removing unused Gradle build file. --- .../kotlin/_build.gradle | 47 ------------------- .../kotlin/build.gradle.kts | 1 - 2 files changed, 48 deletions(-) delete mode 100644 examples/powertools-examples-core/kotlin/_build.gradle diff --git a/examples/powertools-examples-core/kotlin/_build.gradle b/examples/powertools-examples-core/kotlin/_build.gradle deleted file mode 100644 index 885de6f43..000000000 --- a/examples/powertools-examples-core/kotlin/_build.gradle +++ /dev/null @@ -1,47 +0,0 @@ - -plugins { - id 'java' - id "io.freefair.aspectj.post-compile-weaving" version "6.6.3" - id 'org.jetbrains.kotlin.jvm' version '1.9.10' -} - -wrapper { - gradleVersion = "7.6.1" -} - -compileJava { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - - ajc { - enabled = true - } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'com.amazonaws:aws-lambda-java-core:1.2.2' - implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' - 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' - testImplementation 'junit:junit:4.13.2' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" -} -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} - diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index a1a9e29da..81237f07a 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - java id("io.freefair.aspectj.post-compile-weaving") version "6.6.3" kotlin("jvm") version "1.9.10" } From 3f3f4e8b43129b23197a4e40974745224fd3746f Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 13:54:01 +0100 Subject: [PATCH 20/59] Adding SAM template so can be used as an existing project and Java target compatibility --- .../kotlin/build.gradle.kts | 4 ++ .../kotlin/template.yaml | 71 ------------------- 2 files changed, 4 insertions(+), 71 deletions(-) delete mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index 81237f07a..b89b93526 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -25,3 +25,7 @@ tasks.compileKotlin { jvmTarget = "1.8" } } + +java { + targetCompatibility = JavaVersion.VERSION_1_8 +} diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml deleted file mode 100644 index a717c2998..000000000 --- a/examples/powertools-examples-core/kotlin/template.yaml +++ /dev/null @@ -1,71 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - gradle - - Sample SAM Template for gradle - -# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst -Globals: - Function: - Timeout: 20 - Runtime: java11 - MemorySize: 512 - Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html - Environment: - Variables: - # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables - POWERTOOLS_LOG_LEVEL: INFO - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 - POWERTOOLS_LOGGER_LOG_EVENT: true - POWERTOOLS_METRICS_NAMESPACE: Coreutilities - -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction - Properties: - CodeUri: . - Handler: helloworld.App::handleRequest - Runtime: java8 - MemorySize: 512 - Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object - Variables: - POWERTOOLS_SERVICE_NAME: hello - Events: - HelloWorld: - Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api - Properties: - Path: /hello - Method: get - - HelloWorldStreamFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: . - Handler: helloworld.AppStream::handleRequest - Runtime: java8 - MemorySize: 512 - Tracing: Active - Environment: - Variables: - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 - Events: - HelloWorld: - Type: Api - Properties: - Path: /hellostream - Method: get - -Outputs: - # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function - # Find out more about other implicit resources you can reference within SAM - # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api - HelloWorldApi: - Description: "API Gateway endpoint URL for Prod stage for Hello World function" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" - HelloWorldFunction: - Description: "Hello World Lambda Function ARN" - Value: !GetAtt HelloWorldFunction.Arn - HelloWorldFunctionIamRole: - Description: "Implicit IAM Role created for Hello World function" - Value: !GetAtt HelloWorldFunctionRole.Arn From 9d70b5791194972dec1673c0338b8c8635de2168 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 13:57:03 +0100 Subject: [PATCH 21/59] Adding SAM template so can be used as an existing project --- .../kotlin/template.yaml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 examples/powertools-examples-core/kotlin/template.yaml diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml new file mode 100644 index 000000000..1a1572fca --- /dev/null +++ b/examples/powertools-examples-core/kotlin/template.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + gradle + + Sample SAM Template for gradle + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 20 + Runtime: java11 + MemorySize: 512 + Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html + Environment: + Variables: + # Powertools for AWS Lambda (Java) env vars: https://docs.powertools.aws.dev/lambda/java/#environment-variables + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1 + POWERTOOLS_LOGGER_LOG_EVENT: true + POWERTOOLS_METRICS_NAMESPACE: Coreutilities + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: . + Handler: helloworld.App::handleRequest + Runtime: java11 + MemorySize: 512 + Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object + Variables: + POWERTOOLS_SERVICE_NAME: hello + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + + HelloWorldStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: . + Handler: helloworld.AppStream::handleRequest + Runtime: java11 + MemorySize: 512 + Tracing: Active + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn From bcff275fd3ce72a75c9da9e0e5e2b059ae37309d Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 15:19:15 +0100 Subject: [PATCH 22/59] Updating guidance to use SAM for build and deploy --- examples/powertools-examples-core/kotlin/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/README.md index 6f0510c48..ac1138f4d 100644 --- a/examples/powertools-examples-core/kotlin/README.md +++ b/examples/powertools-examples-core/kotlin/README.md @@ -7,7 +7,6 @@ configuration guide [in the main project README](../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. - For general information on the deployed example itself, you can refer to the parent [README](../README.md) ## Configuration @@ -17,10 +16,10 @@ This file defines the Lambda function to be deployed as well as API Gateway for The build of the project is managed by Gradle, and configured in [build.gradle](_build.gradle). ## Deploy the sample application -To get started, you can use the Gradle wrapper to bootstrap Gradle and run the build: +To get started, you can use the included template with SAM to run the build and deploy to your AWS environment: ```bash -./gradlew build +sam build && sam deploy --guided ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in From 84935c976e9c5750ca608debbf462c4d604b8222 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 15:29:52 +0100 Subject: [PATCH 23/59] Restructuring separate Java and Kotlin examples. --- examples/README.md | 2 +- .../{ => java}/cdk/README.md | 8 ++++---- .../{ => java}/cdk/app/events/event.json | 0 .../{ => java}/cdk/app/pom.xml | 0 .../cdk/app/src/main/java/helloworld/App.java | 0 .../cdk/app/src/main/java/helloworld/AppStream.java | 0 .../cdk/app/src/main/resources/log4j2.xml | 0 .../cdk/app/src/test/java/helloworld/AppTest.java | 0 .../{ => java}/cdk/infra/cdk.json | 0 .../{ => java}/cdk/infra/pom.xml | 0 .../cdk/infra/src/main/java/cdk/CdkApp.java | 0 .../cdk/infra/src/main/java/cdk/CdkStack.java | 0 .../cdk/infra/src/test/java/cdk/CdkStackTest.java | 0 .../{ => java}/gradle/README.md | 6 +++--- .../{ => java}/gradle/build.gradle | 0 .../{ => java}/gradle/events/event.json | 0 .../{ => java}/gradle/gradle/wrapper/.gitignore | 0 .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/gradle/wrapper/gradle-wrapper.properties | 0 .../{ => java}/gradle/gradlew | 0 .../{ => java}/gradle/gradlew.bat | 0 .../gradle/src/main/java/helloworld/App.java | 0 .../gradle/src/main/java/helloworld/AppStream.java | 0 .../gradle/src/test/java/helloworld/AppTest.java | 0 .../{ => java}/gradle/template.yaml | 0 .../{ => java}/sam/README.md | 4 ++-- .../{ => java}/sam/events/event.json | 0 .../powertools-examples-core/{ => java}/sam/pom.xml | 0 .../sam/src/main/java/helloworld/App.java | 0 .../sam/src/main/java/helloworld/AppStream.java | 0 .../{ => java}/sam/src/main/resources/log4j2.xml | 0 .../sam/src/test/java/helloworld/AppTest.java | 0 .../{ => java}/sam/template.yaml | 0 .../{ => java}/serverless/README.md | 4 ++-- .../{ => java}/serverless/pom.xml | 0 .../{ => java}/serverless/serverless.yml | 0 .../serverless/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../serverless/src/main/resources/log4j2.xml | 0 .../src/test/java/helloworld/AppTest.java | 0 .../kotlin/{ => gradle}/README.md | 6 +++--- .../kotlin/{ => gradle}/build.gradle.kts | 0 .../kotlin/{ => gradle}/events/event.json | 0 .../kotlin/gradle/{ => gradle}/wrapper/.gitignore | 0 .../gradle/{ => gradle}/wrapper/gradle-wrapper.jar | Bin .../{ => gradle}/wrapper/gradle-wrapper.properties | 0 .../kotlin/{ => gradle}/gradlew | 0 .../kotlin/{ => gradle}/gradlew.bat | 0 .../{ => gradle}/src/main/kotlin/helloworld/App.kt | 0 .../src/main/kotlin/helloworld/AppStream.kt | 0 .../src/test/kotlin/helloworld/AppTest.kt | 0 .../kotlin/{ => gradle}/template.yaml | 0 52 files changed, 15 insertions(+), 15 deletions(-) rename examples/powertools-examples-core/{ => java}/cdk/README.md (81%) rename examples/powertools-examples-core/{ => java}/cdk/app/events/event.json (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => java}/cdk/app/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/cdk.json (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/src/main/java/cdk/CdkApp.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/src/main/java/cdk/CdkStack.java (100%) rename examples/powertools-examples-core/{ => java}/cdk/infra/src/test/java/cdk/CdkStackTest.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/README.md (89%) rename examples/powertools-examples-core/{ => java}/gradle/build.gradle (100%) rename examples/powertools-examples-core/{ => java}/gradle/events/event.json (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradle/wrapper/.gitignore (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradlew (100%) rename examples/powertools-examples-core/{ => java}/gradle/gradlew.bat (100%) rename examples/powertools-examples-core/{ => java}/gradle/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{ => java}/gradle/template.yaml (100%) rename examples/powertools-examples-core/{ => java}/sam/README.md (87%) rename examples/powertools-examples-core/{ => java}/sam/events/event.json (100%) rename examples/powertools-examples-core/{ => java}/sam/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/sam/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/sam/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/sam/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => java}/sam/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{ => java}/sam/template.yaml (100%) rename examples/powertools-examples-core/{ => java}/serverless/README.md (86%) rename examples/powertools-examples-core/{ => java}/serverless/pom.xml (100%) rename examples/powertools-examples-core/{ => java}/serverless/serverless.yml (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{ => java}/serverless/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/README.md (89%) rename examples/powertools-examples-core/kotlin/{ => gradle}/build.gradle.kts (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/events/event.json (100%) rename examples/powertools-examples-core/kotlin/gradle/{ => gradle}/wrapper/.gitignore (100%) rename examples/powertools-examples-core/kotlin/gradle/{ => gradle}/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/kotlin/gradle/{ => gradle}/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/gradlew (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/gradlew.bat (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/src/main/kotlin/helloworld/App.kt (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/src/main/kotlin/helloworld/AppStream.kt (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/src/test/kotlin/helloworld/AppTest.kt (100%) rename examples/powertools-examples-core/kotlin/{ => gradle}/template.yaml (100%) diff --git a/examples/README.md b/examples/README.md index 52dc0c1e9..73dc7a1bf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,7 +7,7 @@ Each example can be copied from its subdirectory and used independently of the r * [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) + * [CDK](powertools-examples-core/java/cdk) * [Serverless](./powertools-examples-core/serverless) * [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 diff --git a/examples/powertools-examples-core/cdk/README.md b/examples/powertools-examples-core/java/cdk/README.md similarity index 81% rename from examples/powertools-examples-core/cdk/README.md rename to examples/powertools-examples-core/java/cdk/README.md index f15a24168..f53936b40 100644 --- a/examples/powertools-examples-core/cdk/README.md +++ b/examples/powertools-examples-core/java/cdk/README.md @@ -2,13 +2,13 @@ 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) +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 +- [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. diff --git a/examples/powertools-examples-core/cdk/app/events/event.json b/examples/powertools-examples-core/java/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/cdk/app/events/event.json rename to examples/powertools-examples-core/java/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/java/cdk/app/pom.xml similarity index 100% rename from examples/powertools-examples-core/cdk/app/pom.xml rename to examples/powertools-examples-core/java/cdk/app/pom.xml diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml b/examples/powertools-examples-core/java/cdk/app/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/java/cdk/app/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/cdk/app/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/cdk/app/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/cdk/app/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/cdk/infra/cdk.json b/examples/powertools-examples-core/java/cdk/infra/cdk.json similarity index 100% rename from examples/powertools-examples-core/cdk/infra/cdk.json rename to examples/powertools-examples-core/java/cdk/infra/cdk.json diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/java/cdk/infra/pom.xml similarity index 100% rename from examples/powertools-examples-core/cdk/infra/pom.xml rename to examples/powertools-examples-core/java/cdk/infra/pom.xml diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java rename to examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java diff --git a/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java rename to examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java diff --git a/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java similarity index 100% rename from examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java rename to examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java diff --git a/examples/powertools-examples-core/gradle/README.md b/examples/powertools-examples-core/java/gradle/README.md similarity index 89% rename from examples/powertools-examples-core/gradle/README.md rename to examples/powertools-examples-core/java/gradle/README.md index c7c798d5d..a13e8fd3d 100644 --- a/examples/powertools-examples-core/gradle/README.md +++ b/examples/powertools-examples-core/java/gradle/README.md @@ -2,13 +2,13 @@ This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../README.md). +configuration guide [in the main project README](../../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../README.md) +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. @@ -24,7 +24,7 @@ To get started, you can use the Gradle wrapper to bootstrap Gradle and run the b ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../README.md) +[the examples directory](../../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/gradle/build.gradle b/examples/powertools-examples-core/java/gradle/build.gradle similarity index 100% rename from examples/powertools-examples-core/gradle/build.gradle rename to examples/powertools-examples-core/java/gradle/build.gradle diff --git a/examples/powertools-examples-core/gradle/events/event.json b/examples/powertools-examples-core/java/gradle/events/event.json similarity index 100% rename from examples/powertools-examples-core/gradle/events/event.json rename to examples/powertools-examples-core/java/gradle/events/event.json diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/gradle/gradlew b/examples/powertools-examples-core/java/gradle/gradlew similarity index 100% rename from examples/powertools-examples-core/gradle/gradlew rename to examples/powertools-examples-core/java/gradle/gradlew diff --git a/examples/powertools-examples-core/gradle/gradlew.bat b/examples/powertools-examples-core/java/gradle/gradlew.bat similarity index 100% rename from examples/powertools-examples-core/gradle/gradlew.bat rename to examples/powertools-examples-core/java/gradle/gradlew.bat diff --git a/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/gradle/template.yaml b/examples/powertools-examples-core/java/gradle/template.yaml similarity index 100% rename from examples/powertools-examples-core/gradle/template.yaml rename to examples/powertools-examples-core/java/gradle/template.yaml diff --git a/examples/powertools-examples-core/sam/README.md b/examples/powertools-examples-core/java/sam/README.md similarity index 87% rename from examples/powertools-examples-core/sam/README.md rename to examples/powertools-examples-core/java/sam/README.md index 7a4279ae3..3b8c31ebc 100644 --- a/examples/powertools-examples-core/sam/README.md +++ b/examples/powertools-examples-core/java/sam/README.md @@ -2,7 +2,7 @@ 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) +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. @@ -10,7 +10,7 @@ This file defines the Lambda function to be deployed as well as API Gateway for ## Deploy the sample application To deploy the example, check out the instructions for getting -started with SAM in [the examples directory](../../README.md) +started with SAM in [the examples directory](../../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/sam/events/event.json b/examples/powertools-examples-core/java/sam/events/event.json similarity index 100% rename from examples/powertools-examples-core/sam/events/event.json rename to examples/powertools-examples-core/java/sam/events/event.json diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/java/sam/pom.xml similarity index 100% rename from examples/powertools-examples-core/sam/pom.xml rename to examples/powertools-examples-core/java/sam/pom.xml diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/sam/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/sam/template.yaml b/examples/powertools-examples-core/java/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/sam/template.yaml rename to examples/powertools-examples-core/java/sam/template.yaml diff --git a/examples/powertools-examples-core/serverless/README.md b/examples/powertools-examples-core/java/serverless/README.md similarity index 86% rename from examples/powertools-examples-core/serverless/README.md rename to examples/powertools-examples-core/java/serverless/README.md index aec093182..d97e7465a 100644 --- a/examples/powertools-examples-core/serverless/README.md +++ b/examples/powertools-examples-core/java/serverless/README.md @@ -1,11 +1,11 @@ # Powertools for AWS Lambda (Java) - Core Utilities Example with Serverless Framework This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Framework](https://www.serverless.com/framework). -For general information on the deployed example itself, you can refer to the parent [README](../README.md). +For general information on the deployed example itself, you can refer to the parent [README](../../README.md). To install Serverless Framework if you don't have it yet, you can follow the [Getting Started Guide](https://www.serverless.com/framework/docs/getting-started). ## Configuration -Serverless Framework uses [serverless.yml](./serverless.yml) to define the application's AWS resources. +Serverless Framework uses [serverless.yml](serverless.yml) to define the application's AWS resources. This file 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. diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/java/serverless/pom.xml similarity index 100% rename from examples/powertools-examples-core/serverless/pom.xml rename to examples/powertools-examples-core/java/serverless/pom.xml diff --git a/examples/powertools-examples-core/serverless/serverless.yml b/examples/powertools-examples-core/java/serverless/serverless.yml similarity index 100% rename from examples/powertools-examples-core/serverless/serverless.yml rename to examples/powertools-examples-core/java/serverless/serverless.yml diff --git a/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/kotlin/README.md b/examples/powertools-examples-core/kotlin/gradle/README.md similarity index 89% rename from examples/powertools-examples-core/kotlin/README.md rename to examples/powertools-examples-core/kotlin/gradle/README.md index ac1138f4d..14059f178 100644 --- a/examples/powertools-examples-core/kotlin/README.md +++ b/examples/powertools-examples-core/kotlin/gradle/README.md @@ -2,12 +2,12 @@ This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../README.md). +configuration guide [in the main project README](../../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../README.md) +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. @@ -23,7 +23,7 @@ sam build && sam deploy --guided ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../README.md) +[the examples directory](../../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts similarity index 100% rename from examples/powertools-examples-core/kotlin/build.gradle.kts rename to examples/powertools-examples-core/kotlin/gradle/build.gradle.kts diff --git a/examples/powertools-examples-core/kotlin/events/event.json b/examples/powertools-examples-core/kotlin/gradle/events/event.json similarity index 100% rename from examples/powertools-examples-core/kotlin/events/event.json rename to examples/powertools-examples-core/kotlin/gradle/events/event.json diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/kotlin/gradlew b/examples/powertools-examples-core/kotlin/gradle/gradlew similarity index 100% rename from examples/powertools-examples-core/kotlin/gradlew rename to examples/powertools-examples-core/kotlin/gradle/gradlew diff --git a/examples/powertools-examples-core/kotlin/gradlew.bat b/examples/powertools-examples-core/kotlin/gradle/gradlew.bat similarity index 100% rename from examples/powertools-examples-core/kotlin/gradlew.bat rename to examples/powertools-examples-core/kotlin/gradle/gradlew.bat diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt rename to examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt rename to examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt diff --git a/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt rename to examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt diff --git a/examples/powertools-examples-core/kotlin/template.yaml b/examples/powertools-examples-core/kotlin/gradle/template.yaml similarity index 100% rename from examples/powertools-examples-core/kotlin/template.yaml rename to examples/powertools-examples-core/kotlin/gradle/template.yaml From bf6da0d11c2108abde85066923be254d8e338872 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 15:30:50 +0100 Subject: [PATCH 24/59] Updating core examples readme to represent new structure for Java and Kotlin examples. --- examples/powertools-examples-core/README.md | 41 +++++++++------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index f2af999b9..787b691ab 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,35 +1,29 @@ -# Powertools for AWS Lambda (Java) - Core Utilities Example +# Powertools for AWS Lambda - Core Utilities Example -This project demonstrates the Lambda for Powertools Java module - including +This project demonstrates the Lambda for Powertools Java module - including [logging](https://docs.powertools.aws.dev/lambda/java/core/logging/), [tracing](https://docs.powertools.aws.dev/lambda/java/core/tracing/), and [metrics](https://docs.powertools.aws.dev/lambda/java/core/metrics/). -We provide examples for the following infrastructure-as-code tools: - -* [AWS SAM](sam/) -* [AWS CDK](cdk/) -* [Serverless framework](serverless/) - -We also provide an example showing the integration of SAM, Powertools, and Gradle: - -* [AWS SAM with a Gradle build](gradle/) - ## Java -For each of the tools, the example application is the same, and consists of the following files: +* [CDK](java/cdk/) +* [Gradle](java/gradle/) +* [SAM](java/sam/) +* [Serverless](java/serverless/) -- [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. +For each of the tools, the example application is the same, and consists of the following files: +- App.java - Code for the application's Lambda function. +- AppTests.java - Unit tests for the application code. +- events - Invocation events that you can use to invoke the function. Configuration files and deployment process for each tool are described in corresponding README files. ## Kotlin -For each of the tools, the example application is the same, and consists of the following files: +* [Gradle](java/gradle/) -- [App.kt](kotlin/src/main/kotlin/helloworld/App.kt) - Code for the application's Lambda function. -- [AppTests.kt](kotlin/src/test/kotlin/helloworld/AppTest.kt) - Unit tests for the application code. -- [events](kotlin/events/event.json) - Invocation events that you can use to invoke the function. +- App.kt - Code for the application's Lambda function. +- AppTests.kt - Unit tests for the application code. +- events - Invocation events that you can use to invoke the function. Configuration files and deployment process for each tool are described in corresponding README files. @@ -41,14 +35,13 @@ Once the app is deployed, you can invoke the endpoint like this: curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ ``` -The response itself isn't particularly interesting - you will get back some information about your IP address. If +The response itself isn't particularly interesting - you will get back some information about your IP address. If you go to the Lambda Console and locate the lambda you have deployed, then click the "Monitoring" tab you will be able to find: * **View X-Ray traces** - Display the traces captured by the traces module. These include subsegments for the -different function calls within the example + different function calls within the example * **View Cloudwatch logs** - Display the structured logging output of 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](sam/src/main/java/helloworld/App.java). +and `ServerlessAirline`. The values in each of these are published by the code in the respective application's Lambda function. From f67f59af4e3541068c915c63f9e61ffedbc13998 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 16:11:12 +0100 Subject: [PATCH 25/59] Refactoring application code for efficiency, updating build to cover tests too and is more idiomatic and readme to be more descriptive --- examples/powertools-examples-core/README.md | 5 ++++- .../kotlin/gradle/build.gradle.kts | 4 ++-- .../kotlin/gradle/src/main/kotlin/helloworld/App.kt | 8 +++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 787b691ab..3fa3628b8 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -5,13 +5,15 @@ 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/). +The example application is the same, and you can now also use Kotlin! + ## Java * [CDK](java/cdk/) * [Gradle](java/gradle/) * [SAM](java/sam/) * [Serverless](java/serverless/) -For each of the tools, the example application is the same, and consists of the following files: +Example application consists of the following files: - App.java - Code for the application's Lambda function. - AppTests.java - Unit tests for the application code. - events - Invocation events that you can use to invoke the function. @@ -21,6 +23,7 @@ Configuration files and deployment process for each tool are described in corres ## Kotlin * [Gradle](java/gradle/) +Example application consists of the following files: - App.kt - Code for the application's Lambda function. - AppTests.kt - Unit tests for the application code. - events - Invocation events that you can use to invoke the function. diff --git a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts index b89b93526..a6e831d9a 100644 --- a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts @@ -26,6 +26,6 @@ tasks.compileKotlin { } } -java { - targetCompatibility = JavaVersion.VERSION_1_8 +kotlin { + jvmToolchain(8) } diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt index 5b6a45ce8..796f6f999 100644 --- a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt @@ -45,7 +45,7 @@ class App : RequestHandler = HashMap() + val headers = mutableMapOf() headers["Content-Type"] = "application/json" headers["X-Custom-Header"] = "application/json" MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) @@ -65,7 +65,7 @@ class App : RequestHandler + TracingUtils.withSubsegment("loggingResponse") { _: Subsegment? -> val sampled = "log something out" log.info(sampled) log.info(output) @@ -92,7 +92,5 @@ class App : RequestHandler return br.lines().collect(Collectors.joining(System.lineSeparator())) } } - companion object { - private val log = LogManager.getLogger(App::class.java) - } + private val log = LogManager.getLogger(App::class) } From f7288d5b31d33a58a822086d1fffeeffafde6f9b Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 16:34:44 +0100 Subject: [PATCH 26/59] Updating to fix trailing \n --- .../kotlin/gradle/src/main/kotlin/helloworld/App.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt index 796f6f999..0cc8d1098 100644 --- a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt @@ -89,7 +89,9 @@ class App : RequestHandler return br.lines().collect(Collectors.joining(System.lineSeparator())) } + return InputStreamReader(url.openStream()).use { reader -> + reader.readText().trim() + } } private val log = LogManager.getLogger(App::class) From f03b01b3c3f6b3968d26506c6abf8782c7facb24 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 4 Oct 2023 16:45:49 +0100 Subject: [PATCH 27/59] Updating guidance to be more specific for examples --- examples/README.md | 12 ++++++++---- .../powertools-examples-core/kotlin/gradle/README.md | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/README.md b/examples/README.md index 73dc7a1bf..7d779f8f1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,10 +5,14 @@ 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 with different build tools - * [SAM](./powertools-examples-core/sam) - * [CDK](powertools-examples-core/java/cdk) - * [Serverless](./powertools-examples-core/serverless) +* [powertools-examples-core](powertools-examples-core) - Demonstrates the core logging, tracing, and metrics modules with different build tools and languages + * [Java](./powertools-examples-core/java) + * [CDK](./powertools-examples-core/java/cdk) + * [Gradle](./powertools-examples-core/java/gradle) + * [SAM](./powertools-examples-core/java/sam) + * [Serverless](./powertools-examples-core/java/serverless) + * [Kotlin](./powertools-examples-core/kotlin) + * [Gradle](./powertools-examples-core/kotlin/gradle) * [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 diff --git a/examples/powertools-examples-core/kotlin/gradle/README.md b/examples/powertools-examples-core/kotlin/gradle/README.md index 14059f178..d40397a4b 100644 --- a/examples/powertools-examples-core/kotlin/gradle/README.md +++ b/examples/powertools-examples-core/kotlin/gradle/README.md @@ -13,7 +13,8 @@ For general information on the deployed example itself, you can refer to the par 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. -The build of the project is managed by Gradle, and configured in [build.gradle](_build.gradle). +The build of the project is managed by Gradle, and configured in [build.gradle.kts](build.gradle.kts) +. ## Deploy the sample application To get started, you can use the included template with SAM to run the build and deploy to your AWS environment: From e523554db3c7b0e2b3aa0ccef4b382cfe4c2ba83 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 6 Oct 2023 13:17:01 +0100 Subject: [PATCH 28/59] Adopting new mechanism for specifying jvm target. --- .../kotlin/gradle/build.gradle.kts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts index a6e831d9a..68ff9b6d4 100644 --- a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts @@ -26,6 +26,12 @@ tasks.compileKotlin { } } -kotlin { - jvmToolchain(8) +tasks.compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } } +// If using JDK 11 or higher, use the following instead: +//kotlin { +// jvmToolchain(8) +//} \ No newline at end of file From a71e263684d972387471a76ab7a0166374357fb9 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 6 Oct 2023 13:35:59 +0100 Subject: [PATCH 29/59] accommodating new project structure --- examples/README.md | 2 +- examples/pom.xml | 8 ++++---- examples/powertools-examples-core/README.md | 2 +- .../kotlin/{gradle => }/README.md | 6 +++--- .../kotlin/{gradle => }/build.gradle.kts | 1 + .../kotlin/{gradle => }/events/event.json | 0 .../kotlin/gradle/{gradle => }/wrapper/.gitignore | 0 .../gradle/{gradle => }/wrapper/gradle-wrapper.jar | Bin .../{gradle => }/wrapper/gradle-wrapper.properties | 0 .../kotlin/{gradle => }/gradlew | 0 .../kotlin/{gradle => }/gradlew.bat | 0 .../{gradle => }/src/main/kotlin/helloworld/App.kt | 0 .../src/main/kotlin/helloworld/AppStream.kt | 0 .../src/test/kotlin/helloworld/AppTest.kt | 0 .../kotlin/{gradle => }/template.yaml | 0 15 files changed, 10 insertions(+), 9 deletions(-) rename examples/powertools-examples-core/kotlin/{gradle => }/README.md (89%) rename examples/powertools-examples-core/kotlin/{gradle => }/build.gradle.kts (99%) rename examples/powertools-examples-core/kotlin/{gradle => }/events/event.json (100%) rename examples/powertools-examples-core/kotlin/gradle/{gradle => }/wrapper/.gitignore (100%) rename examples/powertools-examples-core/kotlin/gradle/{gradle => }/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/kotlin/gradle/{gradle => }/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/gradlew (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/gradlew.bat (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/src/main/kotlin/helloworld/App.kt (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/src/main/kotlin/helloworld/AppStream.kt (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/src/test/kotlin/helloworld/AppTest.kt (100%) rename examples/powertools-examples-core/kotlin/{gradle => }/template.yaml (100%) diff --git a/examples/README.md b/examples/README.md index 7d779f8f1..cd5c32fa4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,7 +12,7 @@ Each example can be copied from its subdirectory and used independently of the r * [SAM](./powertools-examples-core/java/sam) * [Serverless](./powertools-examples-core/java/serverless) * [Kotlin](./powertools-examples-core/kotlin) - * [Gradle](./powertools-examples-core/kotlin/gradle) + * [Gradle](powertools-examples-core/kotlin/gradle1) * [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 diff --git a/examples/pom.xml b/examples/pom.xml index 810ec1b36..ba7b5ff65 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,10 +29,10 @@ - powertools-examples-core/sam - powertools-examples-core/cdk/app - powertools-examples-core/cdk/infra - powertools-examples-core/serverless + powertools-examples-core/java/sam + powertools-examples-core/java/cdk/app + powertools-examples-core/java/cdk/infra + powertools-examples-core/java/serverless powertools-examples-idempotency powertools-examples-parameters powertools-examples-serialization diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 3fa3628b8..0c9a44a36 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -21,7 +21,7 @@ Example application consists of the following files: Configuration files and deployment process for each tool are described in corresponding README files. ## Kotlin -* [Gradle](java/gradle/) +* [Gradle](kotlin/) Example application consists of the following files: - App.kt - Code for the application's Lambda function. diff --git a/examples/powertools-examples-core/kotlin/gradle/README.md b/examples/powertools-examples-core/kotlin/README.md similarity index 89% rename from examples/powertools-examples-core/kotlin/gradle/README.md rename to examples/powertools-examples-core/kotlin/README.md index d40397a4b..da9ec5b52 100644 --- a/examples/powertools-examples-core/kotlin/gradle/README.md +++ b/examples/powertools-examples-core/kotlin/README.md @@ -2,12 +2,12 @@ This project demonstrates the Lambda for Powertools Kotlin module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../../README.md). +configuration guide [in the main project README](../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../../README.md) +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. @@ -24,7 +24,7 @@ sam build && sam deploy --guided ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../../README.md) +[the examples directory](../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts similarity index 99% rename from examples/powertools-examples-core/kotlin/gradle/build.gradle.kts rename to examples/powertools-examples-core/kotlin/build.gradle.kts index 68ff9b6d4..2a11034b6 100644 --- a/examples/powertools-examples-core/kotlin/gradle/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -31,6 +31,7 @@ tasks.compileTestKotlin { jvmTarget = "1.8" } } + // If using JDK 11 or higher, use the following instead: //kotlin { // jvmToolchain(8) diff --git a/examples/powertools-examples-core/kotlin/gradle/events/event.json b/examples/powertools-examples-core/kotlin/events/event.json similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/events/event.json rename to examples/powertools-examples-core/kotlin/events/event.json diff --git a/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/kotlin/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/kotlin/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/kotlin/gradle/gradlew b/examples/powertools-examples-core/kotlin/gradlew similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradlew rename to examples/powertools-examples-core/kotlin/gradlew diff --git a/examples/powertools-examples-core/kotlin/gradle/gradlew.bat b/examples/powertools-examples-core/kotlin/gradlew.bat similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/gradlew.bat rename to examples/powertools-examples-core/kotlin/gradlew.bat diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/App.kt rename to examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt diff --git a/examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/src/main/kotlin/helloworld/AppStream.kt rename to examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/AppStream.kt diff --git a/examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt b/examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/src/test/kotlin/helloworld/AppTest.kt rename to examples/powertools-examples-core/kotlin/src/test/kotlin/helloworld/AppTest.kt diff --git a/examples/powertools-examples-core/kotlin/gradle/template.yaml b/examples/powertools-examples-core/kotlin/template.yaml similarity index 100% rename from examples/powertools-examples-core/kotlin/gradle/template.yaml rename to examples/powertools-examples-core/kotlin/template.yaml From 51fc239dc8553722dce8b7c8043b0aad015d18eb Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 6 Oct 2023 13:41:06 +0100 Subject: [PATCH 30/59] Fixing link typo after refactoring --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index cd5c32fa4..d593e1f06 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,7 +12,7 @@ Each example can be copied from its subdirectory and used independently of the r * [SAM](./powertools-examples-core/java/sam) * [Serverless](./powertools-examples-core/java/serverless) * [Kotlin](./powertools-examples-core/kotlin) - * [Gradle](powertools-examples-core/kotlin/gradle1) + * [Gradle](powertools-examples-core/kotlin/) * [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 From 6cef711d22700353b420c90f20e2ed90ccb98398 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 13 Oct 2023 14:54:13 +0100 Subject: [PATCH 31/59] Flattening structure back to original to make merging easier for v2 --- examples/README.md | 10 +- examples/pom.xml | 8 +- .../{java => }/cdk/README.md | 0 .../{java => }/cdk/app/events/event.json | 0 .../{java => }/cdk/app/pom.xml | 0 .../cdk/app/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../cdk/app/src/main/resources/log4j2.xml | 0 .../app/src/test/java/helloworld/AppTest.java | 0 .../{java => }/cdk/infra/cdk.json | 0 .../{java => }/cdk/infra/pom.xml | 0 .../cdk/infra/src/main/java/cdk/CdkApp.java | 0 .../cdk/infra/src/main/java/cdk/CdkStack.java | 0 .../infra/src/test/java/cdk/CdkStackTest.java | 0 .../{java => }/gradle/README.md | 0 .../{java => }/gradle/build.gradle | 0 .../{java => }/gradle/events/event.json | 0 .../gradle/gradle/wrapper/.gitignore | 0 .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../{java => }/gradle/gradlew | 0 .../{java => }/gradle/gradlew.bat | 184 +++++++++--------- .../gradle/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../src/test/java/helloworld/AppTest.java | 0 .../{java => }/gradle/template.yaml | 0 .../{java => }/sam/README.md | 0 .../{java => }/sam/events/event.json | 0 .../{java => }/sam/pom.xml | 0 .../sam/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../sam/src/main/resources/log4j2.xml | 0 .../sam/src/test/java/helloworld/AppTest.java | 0 .../{java => }/sam/template.yaml | 0 .../{java => }/serverless/README.md | 0 .../{java => }/serverless/pom.xml | 0 .../{java => }/serverless/serverless.yml | 0 .../src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../serverless/src/main/resources/log4j2.xml | 0 .../src/test/java/helloworld/AppTest.java | 0 41 files changed, 100 insertions(+), 102 deletions(-) rename examples/powertools-examples-core/{java => }/cdk/README.md (100%) rename examples/powertools-examples-core/{java => }/cdk/app/events/event.json (100%) rename examples/powertools-examples-core/{java => }/cdk/app/pom.xml (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/cdk.json (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/pom.xml (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/src/main/java/cdk/CdkApp.java (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/src/main/java/cdk/CdkStack.java (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/src/test/java/cdk/CdkStackTest.java (100%) rename examples/powertools-examples-core/{java => }/gradle/README.md (100%) rename examples/powertools-examples-core/{java => }/gradle/build.gradle (100%) rename examples/powertools-examples-core/{java => }/gradle/events/event.json (100%) rename examples/powertools-examples-core/{java => }/gradle/gradle/wrapper/.gitignore (100%) rename examples/powertools-examples-core/{java => }/gradle/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/{java => }/gradle/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/{java => }/gradle/gradlew (100%) rename examples/powertools-examples-core/{java => }/gradle/gradlew.bat (96%) rename examples/powertools-examples-core/{java => }/gradle/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/gradle/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/gradle/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{java => }/gradle/template.yaml (100%) rename examples/powertools-examples-core/{java => }/sam/README.md (100%) rename examples/powertools-examples-core/{java => }/sam/events/event.json (100%) rename examples/powertools-examples-core/{java => }/sam/pom.xml (100%) rename examples/powertools-examples-core/{java => }/sam/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/sam/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/sam/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{java => }/sam/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{java => }/sam/template.yaml (100%) rename examples/powertools-examples-core/{java => }/serverless/README.md (100%) rename examples/powertools-examples-core/{java => }/serverless/pom.xml (100%) rename examples/powertools-examples-core/{java => }/serverless/serverless.yml (100%) rename examples/powertools-examples-core/{java => }/serverless/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/serverless/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/serverless/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{java => }/serverless/src/test/java/helloworld/AppTest.java (100%) diff --git a/examples/README.md b/examples/README.md index d593e1f06..6dbe00185 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,13 +6,11 @@ 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 with different build tools and languages - * [Java](./powertools-examples-core/java) - * [CDK](./powertools-examples-core/java/cdk) - * [Gradle](./powertools-examples-core/java/gradle) - * [SAM](./powertools-examples-core/java/sam) - * [Serverless](./powertools-examples-core/java/serverless) + * [CDK](./powertools-examples-core/cdk) + * [Gradle](./powertools-examples-core/gradle) + * [SAM](./powertools-examples-core/sam) + * [Serverless](./powertools-examples-core/serverless) * [Kotlin](./powertools-examples-core/kotlin) - * [Gradle](powertools-examples-core/kotlin/) * [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 diff --git a/examples/pom.xml b/examples/pom.xml index ba7b5ff65..810ec1b36 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,10 +29,10 @@ - powertools-examples-core/java/sam - powertools-examples-core/java/cdk/app - powertools-examples-core/java/cdk/infra - powertools-examples-core/java/serverless + powertools-examples-core/sam + powertools-examples-core/cdk/app + powertools-examples-core/cdk/infra + powertools-examples-core/serverless powertools-examples-idempotency powertools-examples-parameters powertools-examples-serialization diff --git a/examples/powertools-examples-core/java/cdk/README.md b/examples/powertools-examples-core/cdk/README.md similarity index 100% rename from examples/powertools-examples-core/java/cdk/README.md rename to examples/powertools-examples-core/cdk/README.md diff --git a/examples/powertools-examples-core/java/cdk/app/events/event.json b/examples/powertools-examples-core/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/java/cdk/app/events/event.json rename to examples/powertools-examples-core/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/java/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/cdk/app/pom.xml rename to examples/powertools-examples-core/cdk/app/pom.xml diff --git a/examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/infra/cdk.json b/examples/powertools-examples-core/cdk/infra/cdk.json similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/cdk.json rename to examples/powertools-examples-core/cdk/infra/cdk.json diff --git a/examples/powertools-examples-core/java/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/pom.xml rename to examples/powertools-examples-core/cdk/infra/pom.xml diff --git a/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java rename to examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java diff --git a/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java rename to examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java diff --git a/examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java rename to examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java diff --git a/examples/powertools-examples-core/java/gradle/README.md b/examples/powertools-examples-core/gradle/README.md similarity index 100% rename from examples/powertools-examples-core/java/gradle/README.md rename to examples/powertools-examples-core/gradle/README.md diff --git a/examples/powertools-examples-core/java/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle similarity index 100% rename from examples/powertools-examples-core/java/gradle/build.gradle rename to examples/powertools-examples-core/gradle/build.gradle diff --git a/examples/powertools-examples-core/java/gradle/events/event.json b/examples/powertools-examples-core/gradle/events/event.json similarity index 100% rename from examples/powertools-examples-core/java/gradle/events/event.json rename to examples/powertools-examples-core/gradle/events/event.json diff --git a/examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/java/gradle/gradlew b/examples/powertools-examples-core/gradle/gradlew similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradlew rename to examples/powertools-examples-core/gradle/gradlew diff --git a/examples/powertools-examples-core/java/gradle/gradlew.bat b/examples/powertools-examples-core/gradle/gradlew.bat similarity index 96% rename from examples/powertools-examples-core/java/gradle/gradlew.bat rename to examples/powertools-examples-core/gradle/gradlew.bat index 6689b85be..93e3f59f1 100644 --- a/examples/powertools-examples-core/java/gradle/gradlew.bat +++ b/examples/powertools-examples-core/gradle/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/java/gradle/template.yaml b/examples/powertools-examples-core/gradle/template.yaml similarity index 100% rename from examples/powertools-examples-core/java/gradle/template.yaml rename to examples/powertools-examples-core/gradle/template.yaml diff --git a/examples/powertools-examples-core/java/sam/README.md b/examples/powertools-examples-core/sam/README.md similarity index 100% rename from examples/powertools-examples-core/java/sam/README.md rename to examples/powertools-examples-core/sam/README.md diff --git a/examples/powertools-examples-core/java/sam/events/event.json b/examples/powertools-examples-core/sam/events/event.json similarity index 100% rename from examples/powertools-examples-core/java/sam/events/event.json rename to examples/powertools-examples-core/sam/events/event.json diff --git a/examples/powertools-examples-core/java/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/sam/pom.xml rename to examples/powertools-examples-core/sam/pom.xml diff --git a/examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/sam/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/sam/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/java/sam/template.yaml b/examples/powertools-examples-core/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/java/sam/template.yaml rename to examples/powertools-examples-core/sam/template.yaml diff --git a/examples/powertools-examples-core/java/serverless/README.md b/examples/powertools-examples-core/serverless/README.md similarity index 100% rename from examples/powertools-examples-core/java/serverless/README.md rename to examples/powertools-examples-core/serverless/README.md diff --git a/examples/powertools-examples-core/java/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/serverless/pom.xml rename to examples/powertools-examples-core/serverless/pom.xml diff --git a/examples/powertools-examples-core/java/serverless/serverless.yml b/examples/powertools-examples-core/serverless/serverless.yml similarity index 100% rename from examples/powertools-examples-core/java/serverless/serverless.yml rename to examples/powertools-examples-core/serverless/serverless.yml diff --git a/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java From c24ee6d9c0677dcb11f29575d873a3190b114027 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 13 Oct 2023 15:06:41 +0100 Subject: [PATCH 32/59] Adding build for Kotlin Gradle --- .github/workflows/pr_build.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 73ae67553..0a9988712 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -63,11 +63,15 @@ jobs: cache: 'maven' - name: Build with Maven run: mvn -B install --file pom.xml - - name: Build Gradle Example + - name: Build Gradle Example - Java if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 run: | cd examples/powertools-examples-core/gradle ./gradlew build + - name: Build Gradle Example - Kotlin + run: | + cd examples/powertools-examples-core/kotlin + ./gradlew build - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once From de19b56e9e0a443af868db9e28809e57957641a6 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 13 Oct 2023 15:21:19 +0100 Subject: [PATCH 33/59] Adding build for Kotlin Gradle - Restructuring Java examples to v1 approach --- examples/powertools-examples-core/README.md | 8 +- .../{java => }/cdk/README.md | 2 +- .../{java => }/cdk/app/events/event.json | 0 .../{java => }/cdk/app/pom.xml | 0 .../cdk/app/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../cdk/app/src/main/resources/log4j2.xml | 0 .../app/src/test/java/helloworld/AppTest.java | 0 .../{java => }/cdk/infra/cdk.json | 0 .../{java => }/cdk/infra/pom.xml | 0 .../cdk/infra/src/main/java/cdk/CdkApp.java | 0 .../cdk/infra/src/main/java/cdk/CdkStack.java | 0 .../infra/src/test/java/cdk/CdkStackTest.java | 0 .../{java => }/gradle/README.md | 6 +- .../{java => }/gradle/build.gradle | 0 .../{java => }/gradle/events/event.json | 0 .../gradle/gradle/wrapper/.gitignore | 0 .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../{java => }/gradle/gradlew | 0 .../{java => }/gradle/gradlew.bat | 184 +++++++++--------- .../gradle/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../src/test/java/helloworld/AppTest.java | 0 .../{java => }/gradle/template.yaml | 0 .../kotlin/build.gradle.kts | 6 +- .../{java => }/sam/README.md | 4 +- .../{java => }/sam/events/event.json | 0 .../{java => }/sam/pom.xml | 0 .../sam/src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../sam/src/main/resources/log4j2.xml | 0 .../sam/src/test/java/helloworld/AppTest.java | 0 .../{java => }/sam/template.yaml | 0 .../{java => }/serverless/README.md | 2 +- .../{java => }/serverless/pom.xml | 0 .../{java => }/serverless/serverless.yml | 0 .../src/main/java/helloworld/App.java | 0 .../src/main/java/helloworld/AppStream.java | 0 .../serverless/src/main/resources/log4j2.xml | 0 .../src/test/java/helloworld/AppTest.java | 0 41 files changed, 106 insertions(+), 106 deletions(-) rename examples/powertools-examples-core/{java => }/cdk/README.md (97%) rename examples/powertools-examples-core/{java => }/cdk/app/events/event.json (100%) rename examples/powertools-examples-core/{java => }/cdk/app/pom.xml (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{java => }/cdk/app/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/cdk.json (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/pom.xml (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/src/main/java/cdk/CdkApp.java (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/src/main/java/cdk/CdkStack.java (100%) rename examples/powertools-examples-core/{java => }/cdk/infra/src/test/java/cdk/CdkStackTest.java (100%) rename examples/powertools-examples-core/{java => }/gradle/README.md (89%) rename examples/powertools-examples-core/{java => }/gradle/build.gradle (100%) rename examples/powertools-examples-core/{java => }/gradle/events/event.json (100%) rename examples/powertools-examples-core/{java => }/gradle/gradle/wrapper/.gitignore (100%) rename examples/powertools-examples-core/{java => }/gradle/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/powertools-examples-core/{java => }/gradle/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/powertools-examples-core/{java => }/gradle/gradlew (100%) rename examples/powertools-examples-core/{java => }/gradle/gradlew.bat (96%) rename examples/powertools-examples-core/{java => }/gradle/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/gradle/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/gradle/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{java => }/gradle/template.yaml (100%) rename examples/powertools-examples-core/{java => }/sam/README.md (87%) rename examples/powertools-examples-core/{java => }/sam/events/event.json (100%) rename examples/powertools-examples-core/{java => }/sam/pom.xml (100%) rename examples/powertools-examples-core/{java => }/sam/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/sam/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/sam/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{java => }/sam/src/test/java/helloworld/AppTest.java (100%) rename examples/powertools-examples-core/{java => }/sam/template.yaml (100%) rename examples/powertools-examples-core/{java => }/serverless/README.md (95%) rename examples/powertools-examples-core/{java => }/serverless/pom.xml (100%) rename examples/powertools-examples-core/{java => }/serverless/serverless.yml (100%) rename examples/powertools-examples-core/{java => }/serverless/src/main/java/helloworld/App.java (100%) rename examples/powertools-examples-core/{java => }/serverless/src/main/java/helloworld/AppStream.java (100%) rename examples/powertools-examples-core/{java => }/serverless/src/main/resources/log4j2.xml (100%) rename examples/powertools-examples-core/{java => }/serverless/src/test/java/helloworld/AppTest.java (100%) diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index 0c9a44a36..b2dd7bf21 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -8,10 +8,10 @@ This project demonstrates the Lambda for Powertools Java module - including The example application is the same, and you can now also use Kotlin! ## Java -* [CDK](java/cdk/) -* [Gradle](java/gradle/) -* [SAM](java/sam/) -* [Serverless](java/serverless/) +* [CDK](cdk/) +* [Gradle](gradle/) +* [SAM](sam/) +* [Serverless](serverless/) Example application consists of the following files: - App.java - Code for the application's Lambda function. diff --git a/examples/powertools-examples-core/java/cdk/README.md b/examples/powertools-examples-core/cdk/README.md similarity index 97% rename from examples/powertools-examples-core/java/cdk/README.md rename to examples/powertools-examples-core/cdk/README.md index f53936b40..fbc558943 100644 --- a/examples/powertools-examples-core/java/cdk/README.md +++ b/examples/powertools-examples-core/cdk/README.md @@ -2,7 +2,7 @@ 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) +For general information on the deployed example itself, you can refer to the parent [README](../README.md) ## Configuration CDK uses the following project structure: diff --git a/examples/powertools-examples-core/java/cdk/app/events/event.json b/examples/powertools-examples-core/cdk/app/events/event.json similarity index 100% rename from examples/powertools-examples-core/java/cdk/app/events/event.json rename to examples/powertools-examples-core/cdk/app/events/event.json diff --git a/examples/powertools-examples-core/java/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/cdk/app/pom.xml rename to examples/powertools-examples-core/cdk/app/pom.xml diff --git a/examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java b/examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/app/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/cdk/app/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/app/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/java/cdk/infra/cdk.json b/examples/powertools-examples-core/cdk/infra/cdk.json similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/cdk.json rename to examples/powertools-examples-core/cdk/infra/cdk.json diff --git a/examples/powertools-examples-core/java/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/pom.xml rename to examples/powertools-examples-core/cdk/infra/pom.xml diff --git a/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkApp.java rename to examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkApp.java diff --git a/examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java b/examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/src/main/java/cdk/CdkStack.java rename to examples/powertools-examples-core/cdk/infra/src/main/java/cdk/CdkStack.java diff --git a/examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java b/examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java similarity index 100% rename from examples/powertools-examples-core/java/cdk/infra/src/test/java/cdk/CdkStackTest.java rename to examples/powertools-examples-core/cdk/infra/src/test/java/cdk/CdkStackTest.java diff --git a/examples/powertools-examples-core/java/gradle/README.md b/examples/powertools-examples-core/gradle/README.md similarity index 89% rename from examples/powertools-examples-core/java/gradle/README.md rename to examples/powertools-examples-core/gradle/README.md index a13e8fd3d..c7c798d5d 100644 --- a/examples/powertools-examples-core/java/gradle/README.md +++ b/examples/powertools-examples-core/gradle/README.md @@ -2,13 +2,13 @@ This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Application Model](https://aws.amazon.com/serverless/sam/) with [Gradle](https://gradle.org/) running the build. This example is configured for Java 1.8 only; in order to use a newer version, check out the Gradle -configuration guide [in the main project README](../../../../README.md). +configuration guide [in the main project README](../../../README.md). You can also use `sam init` to create a new Gradle-powered Powertools application - choose to use the **AWS Quick Start Templates**, and then **Hello World Example with Powertools for AWS Lambda**, **Java 17** runtime, and finally **gradle**. -For general information on the deployed example itself, you can refer to the parent [README](../../README.md) +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. @@ -24,7 +24,7 @@ To get started, you can use the Gradle wrapper to bootstrap Gradle and run the b ``` Once this is done to deploy the example, check out the instructions for getting started with SAM in -[the examples directory](../../../README.md) +[the examples directory](../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/java/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle similarity index 100% rename from examples/powertools-examples-core/java/gradle/build.gradle rename to examples/powertools-examples-core/gradle/build.gradle diff --git a/examples/powertools-examples-core/java/gradle/events/event.json b/examples/powertools-examples-core/gradle/events/event.json similarity index 100% rename from examples/powertools-examples-core/java/gradle/events/event.json rename to examples/powertools-examples-core/gradle/events/event.json diff --git a/examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore b/examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradle/wrapper/.gitignore rename to examples/powertools-examples-core/gradle/gradle/wrapper/.gitignore diff --git a/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.jar rename to examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradle/wrapper/gradle-wrapper.properties rename to examples/powertools-examples-core/gradle/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/powertools-examples-core/java/gradle/gradlew b/examples/powertools-examples-core/gradle/gradlew similarity index 100% rename from examples/powertools-examples-core/java/gradle/gradlew rename to examples/powertools-examples-core/gradle/gradlew diff --git a/examples/powertools-examples-core/java/gradle/gradlew.bat b/examples/powertools-examples-core/gradle/gradlew.bat similarity index 96% rename from examples/powertools-examples-core/java/gradle/gradlew.bat rename to examples/powertools-examples-core/gradle/gradlew.bat index 6689b85be..93e3f59f1 100644 --- a/examples/powertools-examples-core/java/gradle/gradlew.bat +++ b/examples/powertools-examples-core/gradle/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/gradle/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/gradle/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/java/gradle/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/gradle/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/java/gradle/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/gradle/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/java/gradle/template.yaml b/examples/powertools-examples-core/gradle/template.yaml similarity index 100% rename from examples/powertools-examples-core/java/gradle/template.yaml rename to examples/powertools-examples-core/gradle/template.yaml diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index 2a11034b6..7b2342e98 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -13,9 +13,9 @@ 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:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-logging:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-metrics:1.18.0-SNAPSHOT") testImplementation("junit:junit:4.13.2") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } diff --git a/examples/powertools-examples-core/java/sam/README.md b/examples/powertools-examples-core/sam/README.md similarity index 87% rename from examples/powertools-examples-core/java/sam/README.md rename to examples/powertools-examples-core/sam/README.md index 3b8c31ebc..7a4279ae3 100644 --- a/examples/powertools-examples-core/java/sam/README.md +++ b/examples/powertools-examples-core/sam/README.md @@ -2,7 +2,7 @@ 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) +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. @@ -10,7 +10,7 @@ This file defines the Lambda function to be deployed as well as API Gateway for ## Deploy the sample application To deploy the example, check out the instructions for getting -started with SAM in [the examples directory](../../../README.md) +started with SAM in [the examples directory](../../README.md) ## Additional notes diff --git a/examples/powertools-examples-core/java/sam/events/event.json b/examples/powertools-examples-core/sam/events/event.json similarity index 100% rename from examples/powertools-examples-core/java/sam/events/event.json rename to examples/powertools-examples-core/sam/events/event.json diff --git a/examples/powertools-examples-core/java/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/sam/pom.xml rename to examples/powertools-examples-core/sam/pom.xml diff --git a/examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/sam/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/sam/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/java/sam/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-core/sam/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/java/sam/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/sam/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/java/sam/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/sam/src/test/java/helloworld/AppTest.java diff --git a/examples/powertools-examples-core/java/sam/template.yaml b/examples/powertools-examples-core/sam/template.yaml similarity index 100% rename from examples/powertools-examples-core/java/sam/template.yaml rename to examples/powertools-examples-core/sam/template.yaml diff --git a/examples/powertools-examples-core/java/serverless/README.md b/examples/powertools-examples-core/serverless/README.md similarity index 95% rename from examples/powertools-examples-core/java/serverless/README.md rename to examples/powertools-examples-core/serverless/README.md index d97e7465a..9e33aa9ff 100644 --- a/examples/powertools-examples-core/java/serverless/README.md +++ b/examples/powertools-examples-core/serverless/README.md @@ -1,7 +1,7 @@ # Powertools for AWS Lambda (Java) - Core Utilities Example with Serverless Framework This project demonstrates the Lambda for Powertools Java module deployed using [Serverless Framework](https://www.serverless.com/framework). -For general information on the deployed example itself, you can refer to the parent [README](../../README.md). +For general information on the deployed example itself, you can refer to the parent [README](../README.md). To install Serverless Framework if you don't have it yet, you can follow the [Getting Started Guide](https://www.serverless.com/framework/docs/getting-started). ## Configuration diff --git a/examples/powertools-examples-core/java/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml similarity index 100% rename from examples/powertools-examples-core/java/serverless/pom.xml rename to examples/powertools-examples-core/serverless/pom.xml diff --git a/examples/powertools-examples-core/java/serverless/serverless.yml b/examples/powertools-examples-core/serverless/serverless.yml similarity index 100% rename from examples/powertools-examples-core/java/serverless/serverless.yml rename to examples/powertools-examples-core/serverless/serverless.yml diff --git a/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/main/java/helloworld/App.java rename to examples/powertools-examples-core/serverless/src/main/java/helloworld/App.java diff --git a/examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/main/java/helloworld/AppStream.java rename to examples/powertools-examples-core/serverless/src/main/java/helloworld/AppStream.java diff --git a/examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml b/examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/main/resources/log4j2.xml rename to examples/powertools-examples-core/serverless/src/main/resources/log4j2.xml diff --git a/examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java similarity index 100% rename from examples/powertools-examples-core/java/serverless/src/test/java/helloworld/AppTest.java rename to examples/powertools-examples-core/serverless/src/test/java/helloworld/AppTest.java From 6e23b2697a41a05fc01fa2b0ceb06b6322f6c85d Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 13 Oct 2023 15:25:17 +0100 Subject: [PATCH 34/59] Correcting paths --- examples/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index ba7b5ff65..810ec1b36 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -29,10 +29,10 @@ - powertools-examples-core/java/sam - powertools-examples-core/java/cdk/app - powertools-examples-core/java/cdk/infra - powertools-examples-core/java/serverless + powertools-examples-core/sam + powertools-examples-core/cdk/app + powertools-examples-core/cdk/infra + powertools-examples-core/serverless powertools-examples-idempotency powertools-examples-parameters powertools-examples-serialization From 480dd8b93d61098af7a07b5a0a202a9d6d7dd7ba Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 13 Oct 2023 16:10:33 +0100 Subject: [PATCH 35/59] Adding SNAPSHOT support and local capability for Maven. Testing using Java 1.8 --- examples/powertools-examples-core/kotlin/build.gradle.kts | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index 7b2342e98..77cffbb06 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -4,6 +4,7 @@ plugins { } repositories { + mavenLocal() mavenCentral() } diff --git a/pom.xml b/pom.xml index fd31bef37..e6ddbf371 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ powertools-large-messages powertools-e2e-tests powertools-batch - examples + From 7a8dc166b18383b54009e78110f3ddb969c250c6 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 20 Oct 2023 14:54:23 +0100 Subject: [PATCH 36/59] Reviewed and updated against PR comments. --- examples/powertools-examples-core/README.md | 24 +++++++++++-------- .../kotlin/build.gradle.kts | 2 +- .../kotlin/src/main/kotlin/helloworld/App.kt | 6 ++--- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/examples/powertools-examples-core/README.md b/examples/powertools-examples-core/README.md index b2dd7bf21..108187640 100644 --- a/examples/powertools-examples-core/README.md +++ b/examples/powertools-examples-core/README.md @@ -1,4 +1,4 @@ -# Powertools for AWS Lambda - Core Utilities Example +# Powertools for AWS Lambda (Java) - Core Utilities Example This project demonstrates the Lambda for Powertools Java module - including [logging](https://docs.powertools.aws.dev/lambda/java/core/logging/), @@ -8,12 +8,14 @@ This project demonstrates the Lambda for Powertools Java module - including The example application is the same, and you can now also use Kotlin! ## Java -* [CDK](cdk/) -* [Gradle](gradle/) -* [SAM](sam/) -* [Serverless](serverless/) + +- [CDK](cdk/) +- [Gradle](gradle/) +- [SAM](sam/) +- [Serverless](serverless/) Example application consists of the following files: + - App.java - Code for the application's Lambda function. - AppTests.java - Unit tests for the application code. - events - Invocation events that you can use to invoke the function. @@ -21,9 +23,11 @@ Example application consists of the following files: Configuration files and deployment process for each tool are described in corresponding README files. ## Kotlin -* [Gradle](kotlin/) + +- [Gradle](kotlin/) Example application consists of the following files: + - App.kt - Code for the application's Lambda function. - AppTests.kt - Unit tests for the application code. - events - Invocation events that you can use to invoke the function. @@ -38,13 +42,13 @@ Once the app is deployed, you can invoke the endpoint like this: curl https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/hello/ ``` -The response itself isn't particularly interesting - you will get back some information about your IP address. If +The response itself isn't particularly interesting - you will get back some information about your IP address. If you go to the Lambda Console and locate the lambda you have deployed, then click the "Monitoring" tab you will be able to find: -* **View X-Ray traces** - Display the traces captured by the traces module. These include subsegments for the +- **View X-Ray traces** - Display the traces captured by the traces module. These include subsegments for the different function calls within the example -* **View Cloudwatch logs** - Display the structured logging output of the example +- **View Cloudwatch logs** - Display the structured logging output of the example -Likewise, from the CloudWatch dashboard, under **Metrics**, **all metrics**, you will find the namespaces `Another` +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 the respective application's Lambda function. diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index 77cffbb06..fc363c1b9 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -35,5 +35,5 @@ tasks.compileTestKotlin { // If using JDK 11 or higher, use the following instead: //kotlin { -// jvmToolchain(8) +// jvmToolchain(11) //} \ No newline at end of file diff --git a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt index 0cc8d1098..ed4cf267a 100644 --- a/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt +++ b/examples/powertools-examples-core/kotlin/src/main/kotlin/helloworld/App.kt @@ -44,10 +44,8 @@ class App : RequestHandler() - headers["Content-Type"] = "application/json" - headers["X-Custom-Header"] = "application/json" + override fun handleRequest(input: APIGatewayProxyRequestEvent?, context: Context?): APIGatewayProxyResponseEvent { + val headers = mapOf("Content-Type" to "application/json", "X-Custom-Header" to "application/json") MetricsUtils.metricsLogger().putMetric("CustomMetric1", 1.0, Unit.COUNT) MetricsUtils.withSingleMetric("CustomMetrics2", 1.0, Unit.COUNT, "Another") { metric: MetricsLogger -> metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")) From 9a154dd52dfc14a41a36907719020f7a3db5dc3b Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Mon, 23 Oct 2023 16:45:11 +0100 Subject: [PATCH 37/59] Un-commenting examples --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 20342aac1..823b09d61 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ powertools-large-messages powertools-e2e-tests powertools-batch - + examples From e8ddb455b0345ba98ae816ed89b797245227cb33 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Tue, 31 Oct 2023 14:31:38 +0000 Subject: [PATCH 38/59] Adding validation step for IaC SAM --- .github/workflows/pr_iac_lint_.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/pr_iac_lint_.yml diff --git a/.github/workflows/pr_iac_lint_.yml b/.github/workflows/pr_iac_lint_.yml new file mode 100644 index 000000000..8aa239afb --- /dev/null +++ b/.github/workflows/pr_iac_lint_.yml @@ -0,0 +1,24 @@ +name: Validate IaC + +on: + pull_request: + branches: + - main + - v2 + paths: + - 'examples/**' +jobs: + linter: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup java JDK 1.8 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 + with: + distribution: 'corretto' + java-version: 8 + - name: Run SAM validator to check syntax of IaC templates + working-directory: examples/powertools-examples-core/sam + run: | + sam build + sam validate --lint From ec17480b80f1d37023ee221b2ba29c9cc42a1657 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 1 Nov 2023 14:53:27 +0000 Subject: [PATCH 39/59] Adding Terraform for Java projects IaC validator and linter --- .github/workflows/pr_build.yml | 21 --------------------- .github/workflows/pr_iac_lint_.yml | 20 +++++++++++++++++++- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index c316c0073..ea336f468 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -79,33 +79,12 @@ jobs: if: ${{ matrix.java == '8' }} # Gradle example can only be built on Java 8 working-directory: examples/powertools-examples-core/kotlin run: ./gradlew build - - name: Setup Terraform - if: ${{ matrix.java == '11' }} - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - name: Setup AWS credentials if: ${{ matrix.java == '11' }} uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} aws-region: ${{ env.AWS_REGION }} - - name: Terraform validate - working-directory: examples/powertools-examples-core/terraform - if: ${{ matrix.java == '11' }} - run: | - terraform -version - terraform init -backend=false - terraform validate - terraform plan - - name: Setup Terraform lint - if: ${{ matrix.java == '11' }} - uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - - name: Terraform lint - working-directory: examples/powertools-examples-core/terraform - if: ${{ matrix.java == '11' }} - run: | - tflint --version - tflint --init - tflint -f compact - name: Upload coverage to Codecov uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 if: ${{ matrix.java == '11' }} # publish results once diff --git a/.github/workflows/pr_iac_lint_.yml b/.github/workflows/pr_iac_lint_.yml index 8aa239afb..5f86005a5 100644 --- a/.github/workflows/pr_iac_lint_.yml +++ b/.github/workflows/pr_iac_lint_.yml @@ -16,9 +16,27 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - java-version: 8 + java-version: 11 - name: Run SAM validator to check syntax of IaC templates working-directory: examples/powertools-examples-core/sam run: | sam build sam validate --lint + - name: Setup Terraform + uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 + - name: Run Terraform validator to check syntax of IaC templates and produce a plan of changes + working-directory: examples/powertools-examples-core/terraform + run: | + mvn install + terraform -version + terraform init -backend=false + terraform validate + terraform plan + - name: Setup Terraform lint + uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 + - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. + working-directory: examples/powertools-examples-core/terraform + run: | + tflint --version + tflint --init + tflint -f compact From 7d11fac3a3bea8784d71c07be9ad5c70dc542451 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 1 Nov 2023 16:03:29 +0000 Subject: [PATCH 40/59] Adding additional projects for SAM validation and matrix approach --- .github/workflows/pr_iac_lint_.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr_iac_lint_.yml b/.github/workflows/pr_iac_lint_.yml index 5f86005a5..209072b02 100644 --- a/.github/workflows/pr_iac_lint_.yml +++ b/.github/workflows/pr_iac_lint_.yml @@ -10,15 +10,18 @@ on: jobs: linter: runs-on: ubuntu-latest + strategy: + matrix: + project: ["sam", "gradle", "kotlin"] steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java JDK 1.8 + - name: Setup java JDK uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' java-version: 11 - - name: Run SAM validator to check syntax of IaC templates - working-directory: examples/powertools-examples-core/sam + - name: Run SAM validator to check syntax of IaC templates - Java + working-directory: examples/powertools-examples-core/${{ matrix.project }} run: | sam build sam validate --lint From e932d109d33d6be16b7961d6d24749294874ee06 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Fri, 17 Nov 2023 16:48:33 +0000 Subject: [PATCH 41/59] Refactoring stream function to process input logging example with a Lambda Function URL instead of APIGW. --- .../src/main/java/helloworld/AppStream.java | 31 +++++++++++++++++-- .../sam/template.yaml | 13 ++++---- 2 files changed, 35 insertions(+), 9 deletions(-) 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 index 401ef8c48..c212db463 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java @@ -17,22 +17,47 @@ 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 org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.metrics.Metrics; +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.Charset; + public class AppStream implements RequestStreamHandler { private static final ObjectMapper mapper = new ObjectMapper(); + private final static Logger log = LogManager.getLogger(AppStream.class); @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()); + BufferedReader reader = new BufferedReader(new InputStreamReader(input, Charset.forName("US-ASCII"))); + PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, Charset.forName("US-ASCII")))); + int nextChar; + try { + while ((nextChar = reader.read()) != -1) { + output.write(nextChar); + } + } catch (IOException e) { + log.error("Something has gone wrong: ", e); + } finally { + reader.close(); + String finalString = writer.toString(); + log.info("Final string result: " + finalString); + writer.close(); + } } } + diff --git a/examples/powertools-examples-core/sam/template.yaml b/examples/powertools-examples-core/sam/template.yaml index 9a51a1ba9..4727222f2 100644 --- a/examples/powertools-examples-core/sam/template.yaml +++ b/examples/powertools-examples-core/sam/template.yaml @@ -44,12 +44,13 @@ Resources: Environment: Variables: POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 - Events: - HelloWorld: - Type: Api - Properties: - Path: /hellostream - Method: get + + HelloWorldStreamFunctionUrl: + Type: AWS::Lambda::Url + Properties: + TargetFunctionArn: !Ref HelloWorldStreamFunction + AuthType: AWS_IAM + InvokeMode: RESPONSE_STREAM Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function From ab31b091d4251447912ec568e0b7bcbad34495b1 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Thu, 23 Nov 2023 11:36:36 +0000 Subject: [PATCH 42/59] Demonstrating Java streaming response --- .../sam/src/main/java/helloworld/AppStream.java | 13 +++++++------ examples/powertools-examples-core/sam/template.yaml | 13 ++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) 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 index c212db463..6446ffe16 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java @@ -35,6 +35,9 @@ import java.io.PrintWriter; import java.nio.charset.Charset; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import software.amazon.lambda.powertools.tracing.TracingUtils; + public class AppStream implements RequestStreamHandler { private static final ObjectMapper mapper = new ObjectMapper(); private final static Logger log = LogManager.getLogger(AppStream.class); @@ -45,17 +48,15 @@ public class AppStream implements RequestStreamHandler { public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(input, Charset.forName("US-ASCII"))); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, Charset.forName("US-ASCII")))); - int nextChar; try { - while ((nextChar = reader.read()) != -1) { - output.write(nextChar); - } + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() + .withStatusCode(200) + .withBody("output_body"); + output.write(response.toString().getBytes()); } catch (IOException e) { log.error("Something has gone wrong: ", e); } finally { reader.close(); - String finalString = writer.toString(); - log.info("Final string result: " + finalString); writer.close(); } } diff --git a/examples/powertools-examples-core/sam/template.yaml b/examples/powertools-examples-core/sam/template.yaml index 4727222f2..9a51a1ba9 100644 --- a/examples/powertools-examples-core/sam/template.yaml +++ b/examples/powertools-examples-core/sam/template.yaml @@ -44,13 +44,12 @@ Resources: Environment: Variables: POWERTOOLS_LOGGER_SAMPLE_RATE: 0.7 - - HelloWorldStreamFunctionUrl: - Type: AWS::Lambda::Url - Properties: - TargetFunctionArn: !Ref HelloWorldStreamFunction - AuthType: AWS_IAM - InvokeMode: RESPONSE_STREAM + Events: + HelloWorld: + Type: Api + Properties: + Path: /hellostream + Method: get Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function From d4bed891098c1ef06348369dc12a484923716c6d Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Tue, 28 Nov 2023 16:23:13 +0000 Subject: [PATCH 43/59] Refactoring stream function to process input logging example to return --- .../src/main/java/helloworld/AppStream.java | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) 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 index 6446ffe16..0158b7e06 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java @@ -21,7 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Map; +import java.nio.charset.StandardCharsets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,10 +33,6 @@ import java.io.BufferedWriter; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.nio.charset.Charset; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import software.amazon.lambda.powertools.tracing.TracingUtils; public class AppStream implements RequestStreamHandler { private static final ObjectMapper mapper = new ObjectMapper(); @@ -45,19 +41,15 @@ public class AppStream implements RequestStreamHandler { @Override @Logging(logEvent = true) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader(input, Charset.forName("US-ASCII"))); - PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, Charset.forName("US-ASCII")))); - try { - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() - .withStatusCode(200) - .withBody("output_body"); - output.write(response.toString().getBytes()); + public void handleRequest(InputStream input, OutputStream output, Context context) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { + + log.info("Received: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readTree(reader))); + + writer.write("{\"body\": \"" + System.currentTimeMillis() + "\"} "); } catch (IOException e) { log.error("Something has gone wrong: ", e); - } finally { - reader.close(); - writer.close(); } } } From 4322ce9d3bbea8d80def6a7e65326f707bf318c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:06:35 +0100 Subject: [PATCH 44/59] Update CONTRIBUTING.md --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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". ![](docs/media/intellij_checkstyle_1.png) -3. Select the code you've created (module, package, class) and reformat code: `⌘⌥L` (macOS), or `Ctrl+Alt+L` (Windows/Linux): -![](docs/media/intellij_checkstyle_2.png) +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: ![](docs/media/intellij_checkstyle_3.png) From 494d73f142823abc04d1123d21f42b35785ff045 Mon Sep 17 00:00:00 2001 From: Michele Ricciardi Date: Wed, 8 Nov 2023 16:35:51 +0100 Subject: [PATCH 45/59] fix: get trace id from system property when env var is not set (#1503) * fix: check if XRAY Trace ID is present in System property * chore: remove erroneous extra char in tests --- .../core/internal/LambdaConstants.java | 1 + .../core/internal/LambdaHandlerProcessor.java | 8 +++- .../core/internal/SystemWrapper.java | 4 ++ .../amazon/lambda/powertools/LoggingE2ET.java | 2 + .../internal/LambdaLoggingAspectTest.java | 21 ++++++++- .../powertools/metrics/MetricsLoggerTest.java | 44 +++++++++++++++++-- .../internal/LambdaMetricsAspectTest.java | 6 +-- 7 files changed, 77 insertions(+), 9 deletions(-) diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java index d0f94260b..e64e334c5 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/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-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index e9e220e41..91c93c3f3 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/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.core.internal.SystemWrapper.getProperty; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; @@ -93,7 +94,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-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java index 30f72232f..99046ffec 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/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/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-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index b78710586..6952fe755 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getProperty; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; @@ -245,13 +246,31 @@ void shouldLogServiceNameWhenEnvVarSet() throws IllegalAccessException { .containsEntry("service", "testService"); } + @Test + void shouldLogxRayTraceIdSystemPropertySet() { + String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; + + try (MockedStatic mocked = mockStatic(SystemWrapper.class)) { + mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn(null); + mocked.when(() -> getProperty("com.amazonaws.xray.traceHeader")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); + + requestHandler.handleRequest(new Object(), context); + + assertThat(ThreadContext.getImmutableContext()) + .hasSize(EXPECTED_CONTEXT_SIZE + 1) + .containsEntry("xray_trace_id", xRayTraceId); + } + } + @Test void shouldLogxRayTraceIdEnvVarSet() { String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; try (MockedStatic mocked = mockStatic(SystemWrapper.class)) { mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); requestHandler.handleRequest(new Object(), context); diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java index 7f234a4d6..16a068849 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNullPointerException; import static org.mockito.Mockito.mockStatic; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getProperty; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.fasterxml.jackson.core.JsonProcessingException; @@ -66,7 +67,7 @@ void singleMetricsCaptureUtilityWithDefaultDimension() { software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.defaultDimensions(DimensionSet.of("Service", "Booking")); @@ -96,7 +97,7 @@ void singleMetricsCaptureUtility() { software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, "test", metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); @@ -123,7 +124,7 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); @@ -165,6 +166,41 @@ void shouldThrowExceptionWhenDefaultDimensionIsNull() { .withMessage("Null dimension set not allowed"); } + @Test + void shouldUseTraceIdFromSystemPropertyIfEnvVarNotPresent() { + try (MockedStatic mocked = mockStatic(SystemWrapper.class); + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); + mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn(null); + internalWrapper.when(() -> getProperty("com.amazonaws.xray.traceHeader")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); + + MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, + metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); + + assertThat(out.toString()) + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); + } + } + private void testLogger(Consumer> methodToTest) { try (MockedStatic mocked = mockStatic(SystemWrapper.class); MockedStatic internalWrapper = mockStatic( @@ -172,7 +208,7 @@ private void testLogger(Consumer> methodToTest) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); methodToTest.accept(metricsLogger -> { diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java index eaddfa75d..0a735c75e 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java @@ -91,7 +91,7 @@ public void metricsWithoutColdStart() { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); MetricsUtils.defaultDimensions(null); requestHandler = new PowertoolsMetricsEnabledHandler(); @@ -135,7 +135,7 @@ public void metricsWithDefaultDimensionSpecified() { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); requestHandler = new PowertoolsMetricsEnabledDefaultDimensionHandler(); @@ -179,7 +179,7 @@ public void metricsWithDefaultNoDimensionSpecified() { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) - .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"); requestHandler = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); From e6e2eb0439e46b2039a17147a1a090f494da84a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:52:08 +0100 Subject: [PATCH 46/59] fix #1500 (#1506) --- .../powertools/metrics/MetricsUtils.java | 8 +++-- .../powertools/metrics/MetricsLoggerTest.java | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java index 09517d46e..7891b22ec 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java @@ -178,8 +178,12 @@ private static void captureRequestAndTraceId(MetricsLogger metricsLogger) { private static String defaultNameSpace() { MetricsContext context = MetricsLoggerHelper.metricsContext(); - return "aws-embedded-metrics".equals(context.getNamespace()) ? - SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE") : context.getNamespace(); + if ("aws-embedded-metrics".equals(context.getNamespace())) { + String namespace = SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE"); + return namespace != null ? namespace : "aws-embedded-metrics"; + } else { + return context.getNamespace(); + } } private static Optional awsRequestId() { diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java index 16a068849..ce9d63cfd 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java @@ -112,6 +112,37 @@ void singleMetricsCaptureUtility() { .containsEntry("Dimension1", "Value1") .containsKey("_aws") .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=test"); + }); + } + } + + @Test + void singleMetricsCaptureUtilityWithNullNamespace() { + try (MockedStatic mocked = mockStatic(SystemWrapper.class); + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); + // POWERTOOLS_METRICS_NAMESPACE is not defined + + MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, + metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); + + assertThat(out.toString()) + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=aws-embedded-metrics"); }); } } From 7c51a38b7aa70ef341fc76486d49d72328076513 Mon Sep 17 00:00:00 2001 From: Alexey Soshin Date: Tue, 14 Nov 2023 14:27:38 +0000 Subject: [PATCH 47/59] feat: Add support for POWERTOOLS_LOGGER_LOG_EVENT (#1510) --- docs/core/logging.md | 4 +- docs/index.md | 2 + .../core/internal/LambdaHandlerProcessor.java | 7 ++ .../logging/internal/LambdaLoggingAspect.java | 14 ++- .../handlers/PowerToolLogEventDisabled.java | 28 +++++ .../internal/LambdaLoggingAspectTest.java | 119 +++++++++++------- 6 files changed, 127 insertions(+), 47 deletions(-) create mode 100644 powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java diff --git a/docs/core/logging.md b/docs/core/logging.md index 2df9a4529..fd74db3d2 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. diff --git a/docs/index.md b/docs/index.md index 6af4c5e8d..92589be7c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -285,5 +285,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/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index 91c93c3f3..d4e18dddc 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -47,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 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 4a98735af..ff556d89a 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 @@ -33,6 +33,7 @@ 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 +43,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; @@ -65,6 +67,7 @@ public final class LambdaLoggingAspect { private static final String LOG_LEVEL = System.getenv("POWERTOOLS_LOG_LEVEL"); private static final String SAMPLING_RATE = System.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE"); + private static Boolean LOG_EVENT; private static Level LEVEL_AT_INITIALISATION; @@ -74,6 +77,13 @@ public final class LambdaLoggingAspect { } 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 +114,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/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java new file mode 100644 index 000000000..77103e450 --- /dev/null +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventDisabled.java @@ -0,0 +1,28 @@ +/* + * 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.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.lambda.powertools.logging.Logging; + +public class PowerToolLogEventDisabled implements RequestHandler { + + @Logging(logEvent = false) + @Override + public Object handleRequest(Object input, Context context) { + return null; + } +} diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index 6952fe755..75cf22aa5 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java @@ -14,19 +14,6 @@ package software.amazon.lambda.powertools.logging.internal; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.joining; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getProperty; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; - import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -37,24 +24,10 @@ import com.amazonaws.services.lambda.runtime.tests.annotations.Event; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.ThreadContext; import org.json.JSONException; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -62,18 +35,33 @@ import org.mockito.MockedStatic; import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.core.internal.SystemWrapper; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolApiGatewayHttpApiCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolApiGatewayRestApiCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabled; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabledForStream; -import software.amazon.lambda.powertools.logging.handlers.PowerToolDisabled; -import software.amazon.lambda.powertools.logging.handlers.PowerToolDisabledForStream; -import software.amazon.lambda.powertools.logging.handlers.PowerToolLogEventEnabled; -import software.amazon.lambda.powertools.logging.handlers.PowerToolLogEventEnabledForStream; -import software.amazon.lambda.powertools.logging.handlers.PowerToolLogEventEnabledWithCustomMapper; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogAlbCorrelationId; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledWithClearState; -import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; +import software.amazon.lambda.powertools.logging.handlers.*; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.joining; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getProperty; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; class LambdaLoggingAspectTest { @@ -116,7 +104,7 @@ void shouldSetLambdaContextWhenEnabled() { void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { requestStreamHandler = new PowerLogToolEnabledForStream(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); assertThat(ThreadContext.getImmutableContext()) @@ -132,14 +120,14 @@ void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { @Test void shouldSetColdStartFlag() throws IOException { - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) .containsEntry("coldStart", "true"); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); assertThat(ThreadContext.getImmutableContext()) @@ -195,6 +183,51 @@ void shouldLogEventForHandler() throws IOException, JSONException { assertEquals(expectEvent, event, false); } + /** + * If POWERTOOLS_LOGGER_LOG_EVENT was set to true, the handler should log, despite @Logging(logEvent=false) + * + * @throws IOException + */ + @Test + void shouldLogEventForHandlerWhenEnvVariableSetToTrue() throws IOException, IllegalAccessException, JSONException { + try { + writeStaticField(LambdaLoggingAspect.class, "LOG_EVENT", Boolean.TRUE, true); + + requestHandler = new PowerToolLogEventDisabled(); + S3EventNotification s3EventNotification = s3EventNotification(); + + requestHandler.handleRequest(s3EventNotification, context); + + Map log = parseToMap(Files.lines(Paths.get("target/logfile.json")).collect(joining())); + + String event = (String) log.get("message"); + + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) + .lines().collect(joining("\n")); + + assertEquals(expectEvent, event, false); + } finally { + writeStaticField(LambdaLoggingAspect.class, "LOG_EVENT", Boolean.FALSE, true); + } + } + + /** + * If POWERTOOLS_LOGGER_LOG_EVENT was set to false and @Logging(logEvent=false), the handler shouldn't log + * + * @throws IOException + */ + @Test + void shouldNotLogEventForHandlerWhenEnvVariableSetToFalse() throws IOException { + requestHandler = new PowerToolLogEventDisabled(); + S3EventNotification s3EventNotification = s3EventNotification(); + + requestHandler.handleRequest(s3EventNotification, context); + + Assertions.assertEquals(0, + Files.lines(Paths.get("target/logfile.json")).collect(joining()).length()); + } + @Test void shouldLogEventForHandlerWithOverriddenObjectMapper() throws IOException, JSONException { RequestHandler handler = new PowerToolLogEventEnabledWithCustomMapper(); From f814d4d9ad2bd8146bd529117649c129a3eb6fff Mon Sep 17 00:00:00 2001 From: jdoherty Date: Tue, 14 Nov 2023 14:29:35 +0000 Subject: [PATCH 48/59] chore: Addition of Warn Message If Invalid Annotation Key While Tracing #1511 (#1512) --- .../powertools/tracing/TracingUtils.java | 21 +++++++++++++++++++ .../powertools/tracing/TracingUtilsTest.java | 21 +++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java index 9fb021548..47c4e7422 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java @@ -21,12 +21,15 @@ import com.amazonaws.xray.entities.Subsegment; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A class of helper functions to add additional functionality and ease * of use. */ public final class TracingUtils { + private static final Logger LOG = LoggerFactory.getLogger(TracingUtils.class); private static ObjectMapper objectMapper; /** @@ -36,6 +39,10 @@ public final class TracingUtils { * @param value the value of the annotation */ public static void putAnnotation(String key, String value) { + if (!isValidAnnotationKey(key)) { + LOG.warn("Ignoring annotation with unsupported characters in key: {}", key); + return; + } AWSXRay.getCurrentSubsegmentOptional() .ifPresent(segment -> segment.putAnnotation(key, value)); } @@ -47,10 +54,24 @@ public static void putAnnotation(String key, String value) { * @param value the value of the annotation */ public static void putAnnotation(String key, Number value) { + if (!isValidAnnotationKey(key)) { + LOG.warn("Ignoring annotation with unsupported characters in key: {}", key); + return; + } AWSXRay.getCurrentSubsegmentOptional() .ifPresent(segment -> segment.putAnnotation(key, value)); } + /** + Make sure that the annotation key is valid according to + the documentation. + + Annotation keys that are added that are invalid are ignored by x-ray. + **/ + private static boolean isValidAnnotationKey(String key) { + return key.matches("^[a-zA-Z0-9_]+$"); + } + /** * Put an annotation to the current subsegment with a Boolean value. * diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java index 78283fbc2..01f25f37a 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java @@ -28,7 +28,6 @@ import org.junit.jupiter.api.Test; class TracingUtilsTest { - @BeforeEach void setUp() { AWSXRay.beginSegment("test"); @@ -123,6 +122,24 @@ void shouldInvokeCodeBlockWrappedWithinSubsegment() { }); } + @Test + void shouldNotAddAnnotationIfInvalidCharacterInKey() { + AWSXRay.beginSubsegment("subSegment"); + String inputKey = "stringKey with spaces"; + TracingUtils.putAnnotation(inputKey, "val"); + AWSXRay.getCurrentSubsegmentOptional() + .ifPresent(segment -> assertThat(segment.getAnnotations()).size().isEqualTo(0)); + } + + @Test + void shouldAddAnnotationIfValidCharactersInKey() { + AWSXRay.beginSubsegment("subSegment"); + String inputKey = "validKey"; + TracingUtils.putAnnotation(inputKey, "val"); + AWSXRay.getCurrentSubsegmentOptional() + .ifPresent(segment -> assertThat(segment.getAnnotations()).size().isEqualTo(1)); + } + @Test void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { Context test = mock(Context.class); @@ -221,4 +238,4 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter .containsEntry("key", "val"); }); } -} \ No newline at end of file +} From 9a3a788db0539d63a808fb746907a26dec1d8983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:23:18 +0100 Subject: [PATCH 49/59] feat: ALC (#1514) * handle AWS_LAMBDA_LOG configuration * ALC documentation + code review * update doc --- docs/core/logging.md | 44 ++++- powertools-logging/pom.xml | 10 ++ .../logging/internal/LambdaLoggingAspect.java | 19 +- .../internal/LambdaTimestampResolver.java | 169 ++++++++++++++++++ .../LambdaTimestampResolverFactory.java | 49 +++++ .../logging/internal/LoggingConstants.java | 27 +++ .../src/main/resources/LambdaJsonLayout.json | 2 +- .../core/layout/LambdaJsonLayoutTest.java | 31 +++- 8 files changed, 344 insertions(+), 7 deletions(-) create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolver.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaTimestampResolverFactory.java create mode 100644 powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LoggingConstants.java diff --git a/docs/core/logging.md b/docs/core/logging.md index fd74db3d2..0391ae7b4 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -263,7 +263,7 @@ When debugging in non-production environments, you can instruct Logger to log th } ``` -### 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: @@ -596,6 +596,48 @@ via `samplingRate` attribute on annotation. POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 ``` +## AWS Lambda Advanced Logging Controls +With AWS [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced), you can control the output format of your logs as either `TEXT` or `JSON` and specify the minimum accepted log level for your application. +Regardless of the output format setting in Lambda, Powertools for AWS Lambda will always output JSON formatted logging messages. + +When you have this feature enabled, log messages that don’t meet the configured log level are discarded by Lambda. +For example, if you set the minimum log level to `WARN`, you will only receive `WARN` and `ERROR` messages in your AWS CloudWatch Logs, all other log levels will be discarded by Lambda. + +```mermaid +sequenceDiagram + participant Lambda service + participant Lambda function + participant Application Logger + + Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN" + Lambda service->>Lambda function: Invoke (event) + Lambda function->>Lambda function: Calls handler + Lambda function->>Application Logger: logger.warn("Something happened") + Lambda function-->>Application Logger: logger.debug("Something happened") + Lambda function-->>Application Logger: logger.info("Something happened") + + Lambda service->>Lambda service: DROP INFO and DEBUG logs + + Lambda service->>CloudWatch Logs: Ingest error logs +``` + +Logger will automatically listen for the `AWS_LAMBDA_LOG_FORMAT` and `AWS_LAMBDA_LOG_LEVEL` environment variables, and change behaviour if they’re found to ensure as much compatibility as possible. + +### Priority of log level settings in Powertools for AWS Lambda + +When the Advanced Logging Controls feature is 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) for more details. + +We prioritise log level settings in this order: + +1. `AWS_LAMBDA_LOG_LEVEL` environment variable +2. `POWERTOOLS_LOG_LEVEL` environment variable + +In the event you have set `POWERTOOLS_LOG_LEVEL` to a level lower than the ACL setting, Powertools for AWS Lambda will output a warning log message informing you that your messages will be discarded by Lambda. + +### 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/powertools-logging/pom.xml b/powertools-logging/pom.xml index c31448e30..e4767893b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -137,6 +137,16 @@ org.apache.maven.plugins maven-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 ff556d89a..da770ccdb 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,6 +28,7 @@ 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; @@ -63,17 +64,27 @@ @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(); 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 handler = new PowerLogToolEnabled(); @Mock @@ -73,6 +77,31 @@ void shouldLogInStructuredFormat() throws IOException { .containsKey("service")); } + @Test + void shouldLogWithRFC3339TimestampFormat_WhenLambdaLoggingIsJSON() throws Exception { + // Given: AWS_LAMBDA_LOG_FORMAT=JSON defined in pom.xml + + // When + handler.handleRequest("test", context); + + // Then + assertThat(Files.lines(Paths.get("target/logfile.json"))) + .hasSize(1) + .allSatisfy(line -> assertThat(parseToMap(line)) + .extracting("timestamp", as(InstanceOfAssertFactories.STRING)) + .satisfies(s -> assertThat(hasDateFormat(s, "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")).isTrue())); + } + + private boolean hasDateFormat(String timestamp, String format) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT); + try { + dtf.parse(timestamp); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + @Test void shouldModifyLogLevelBasedOnEnvVariable() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { From 7fc6ff2ef5c8ac18d5c60016028cd3eda882ab7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:36:51 +0100 Subject: [PATCH 50/59] chore:Prep release 1.18.0 (#1515) * chore:prep release 1.18.0 * update version * update version in kotlin example * maven local repo in gradle example * update changelog --------- Co-authored-by: scottgerring --- CHANGELOG.md | 31 +++++++++++++++++++ README.md | 8 ++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- .../pom.xml | 2 +- .../powertools-examples-core/cdk/app/pom.xml | 2 +- .../cdk/infra/pom.xml | 2 +- .../gradle/build.gradle | 7 +++-- .../kotlin/build.gradle.kts | 6 ++-- examples/powertools-examples-core/sam/pom.xml | 2 +- .../serverless/pom.xml | 2 +- .../terraform/pom.xml | 2 +- .../powertools-examples-idempotency/pom.xml | 2 +- .../powertools-examples-parameters/pom.xml | 2 +- .../powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- .../powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 34 files changed, 72 insertions(+), 40 deletions(-) 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/README.md b/README.md index 900395554..63ba35cc6 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda powertools-tracing - 1.17.0 + 1.18.0 software.amazon.lambda powertools-logging - 1.17.0 + 1.18.0 software.amazon.lambda powertools-metrics - 1.17.0 + 1.18.0 ... @@ -190,7 +190,7 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ## 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/examples/pom.xml b/examples/pom.xml index 900f095f8..428c74af8 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-examples - 1.18.0-SNAPSHOT + 1.18.0 pom Powertools for AWS Lambda (Java) library Examples diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 99d3a97cc..af3655b9a 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-batch jar Powertools for AWS Lambda (Java) library Examples - Batch diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index c195cd262..2df1ce9f6 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-cloudformation jar diff --git a/examples/powertools-examples-core/cdk/app/pom.xml b/examples/powertools-examples-core/cdk/app/pom.xml index b4652bd65..9d9435ac3 100644 --- a/examples/powertools-examples-core/cdk/app/pom.xml +++ b/examples/powertools-examples-core/cdk/app/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.16.1 + 1.17.0 powertools-examples-core-cdk jar diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index 216bf35d4..fd3cd313b 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -4,7 +4,7 @@ 4.0.0 software.amazon.lambda.examples cdk - 1.18.0-SNAPSHOT + 1.18.0 UTF-8 2.100.0 diff --git a/examples/powertools-examples-core/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle index 06aec59e8..0d6b0eee8 100644 --- a/examples/powertools-examples-core/gradle/build.gradle +++ b/examples/powertools-examples-core/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:1.18.0' + aspect 'software.amazon.lambda:powertools-logging:1.18.0' + aspect 'software.amazon.lambda:powertools-metrics:1.18.0' } diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index fc363c1b9..f95d1099d 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -14,9 +14,9 @@ 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.18.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-logging:1.18.0-SNAPSHOT") - aspect("software.amazon.lambda:powertools-metrics:1.18.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-tracing:1.18.0") + aspect("software.amazon.lambda:powertools-logging:1.18.0") + aspect("software.amazon.lambda:powertools-metrics:1.18.0") testImplementation("junit:junit:4.13.2") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index 997472344..adb5e13eb 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-core-sam jar diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 6fce1d2dd..36c7e7280 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-core-serverless jar diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 2a55c31bc..532675717 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-core-terraform jar diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 4b8eba9c9..5e79debf4 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-idempotency jar Powertools for AWS Lambda (Java) library Examples - Idempotency diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 0e07b0cbb..495d6c59c 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-parameters jar Powertools for AWS Lambda (Java) library Examples - Parameters diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index 495fe8b77..e54c84315 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-serialization jar Powertools for AWS Lambda (Java) library Examples - Serialization diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 0338fe7d9..9d0b3f2a8 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-sqs jar Powertools for AWS Lambda (Java) library Examples - SQS diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 65628f6b2..3cb4264c0 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0-SNAPSHOT + 1.18.0 powertools-examples-validation jar Powertools for AWS Lambda (Java) library Examples - Validation diff --git a/mkdocs.yml b/mkdocs.yml index 0f9c065a7..a271c1260 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,7 +88,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 e1a0c5a6c..76b59d747 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-parent - 1.18.0-SNAPSHOT + 1.18.0 pom Powertools for AWS Lambda (Java) library Parent diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 0c59ce497..cedbcf317 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.18.0-SNAPSHOT + 1.18.0 A suite of utilities that makes batch message processing using AWS Lambda easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index f6cd822a6..660f6e34a 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java)library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index b848e9f79..0d82530a0 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) library Core diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 6507e3104..995aeb1c7 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ Fake handlers that use Powertools for AWS Lambda (Java). - 1.18.0-SNAPSHOT + 1.18.0 UTF-8 1.8 1.8 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 84760eac8..d2aeaed66 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 powertools-e2e-tests diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 8b3459dee..fe85d74fd 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-parent - 1.18.0-SNAPSHOT + 1.18.0 powertools-idempotency diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 8fdae2139..2d5540c54 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ software.amazon.lambda powertools-parent - 1.18.0-SNAPSHOT + 1.18.0 powertools-large-messages diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index e4767893b..6fd4ce89f 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index eff7296c8..677e18ed7 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 808232ab1..0e86a0a06 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index e5c4087fe..06b828923 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index c4b2b54f0..e14ec59b5 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 34e80b06e..a6e84c4ac 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index d98a97e2a..f16bedd2f 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 24de0ba2a..a372e4740 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0-SNAPSHOT + 1.18.0 Powertools for AWS Lambda (Java) validation library From 6635b5001e574da721a106f26cd9ee2dfb68d9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:44:08 +0100 Subject: [PATCH 51/59] chore: update version to next snapshot: 1-19.0-SNAPSHOT (#1516) * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * update version to next snapshot: 1-19.0-SNAPSHOT * building only for LTS --- .github/workflows/pr_build.yml | 2 +- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core/cdk/infra/pom.xml | 2 +- examples/powertools-examples-core/gradle/build.gradle | 6 +++--- examples/powertools-examples-core/kotlin/build.gradle.kts | 6 +++--- examples/powertools-examples-core/sam/pom.xml | 2 +- examples/powertools-examples-core/serverless/pom.xml | 2 +- examples/powertools-examples-core/terraform/pom.xml | 2 +- examples/powertools-examples-idempotency/pom.xml | 2 +- examples/powertools-examples-parameters/pom.xml | 2 +- examples/powertools-examples-serialization/pom.xml | 2 +- examples/powertools-examples-sqs/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-core/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-sqs/pom.xml | 2 +- powertools-test-suite/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 31 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index ea336f468..7a02b08d4 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -53,7 +53,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/examples/pom.xml b/examples/pom.xml index 428c74af8..5c423f251 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-examples - 1.18.0 + 1.19.0-SNAPSHOT pom Powertools for AWS Lambda (Java) library Examples diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index af3655b9a..c8f829997 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-batch jar Powertools for AWS Lambda (Java) library Examples - Batch diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 2df1ce9f6..54d6b50ef 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-cloudformation jar diff --git a/examples/powertools-examples-core/cdk/infra/pom.xml b/examples/powertools-examples-core/cdk/infra/pom.xml index fd3cd313b..e2a1d6b9a 100644 --- a/examples/powertools-examples-core/cdk/infra/pom.xml +++ b/examples/powertools-examples-core/cdk/infra/pom.xml @@ -4,7 +4,7 @@ 4.0.0 software.amazon.lambda.examples cdk - 1.18.0 + 1.19.0-SNAPSHOT UTF-8 2.100.0 diff --git a/examples/powertools-examples-core/gradle/build.gradle b/examples/powertools-examples-core/gradle/build.gradle index 0d6b0eee8..520b689cb 100644 --- a/examples/powertools-examples-core/gradle/build.gradle +++ b/examples/powertools-examples-core/gradle/build.gradle @@ -28,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.18.0' - aspect 'software.amazon.lambda:powertools-logging:1.18.0' - aspect 'software.amazon.lambda:powertools-metrics:1.18.0' + aspect 'software.amazon.lambda:powertools-tracing:1.19.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-logging:1.19.0-SNAPSHOT' + aspect 'software.amazon.lambda:powertools-metrics:1.19.0-SNAPSHOT' } diff --git a/examples/powertools-examples-core/kotlin/build.gradle.kts b/examples/powertools-examples-core/kotlin/build.gradle.kts index f95d1099d..4a167f225 100644 --- a/examples/powertools-examples-core/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core/kotlin/build.gradle.kts @@ -14,9 +14,9 @@ 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.18.0") - aspect("software.amazon.lambda:powertools-logging:1.18.0") - aspect("software.amazon.lambda:powertools-metrics:1.18.0") + aspect("software.amazon.lambda:powertools-tracing:1.19.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-logging:1.19.0-SNAPSHOT") + aspect("software.amazon.lambda:powertools-metrics:1.19.0-SNAPSHOT") testImplementation("junit:junit:4.13.2") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") } diff --git a/examples/powertools-examples-core/sam/pom.xml b/examples/powertools-examples-core/sam/pom.xml index adb5e13eb..16f82ebd6 100644 --- a/examples/powertools-examples-core/sam/pom.xml +++ b/examples/powertools-examples-core/sam/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-core-sam jar diff --git a/examples/powertools-examples-core/serverless/pom.xml b/examples/powertools-examples-core/serverless/pom.xml index 36c7e7280..f8d9ab684 100644 --- a/examples/powertools-examples-core/serverless/pom.xml +++ b/examples/powertools-examples-core/serverless/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-core-serverless jar diff --git a/examples/powertools-examples-core/terraform/pom.xml b/examples/powertools-examples-core/terraform/pom.xml index 532675717..eef73207e 100644 --- a/examples/powertools-examples-core/terraform/pom.xml +++ b/examples/powertools-examples-core/terraform/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-core-terraform jar diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 5e79debf4..bf4c041bf 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -17,7 +17,7 @@ 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-idempotency jar Powertools for AWS Lambda (Java) library Examples - Idempotency diff --git a/examples/powertools-examples-parameters/pom.xml b/examples/powertools-examples-parameters/pom.xml index 495d6c59c..2aa813ff4 100644 --- a/examples/powertools-examples-parameters/pom.xml +++ b/examples/powertools-examples-parameters/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-parameters jar Powertools for AWS Lambda (Java) library Examples - Parameters diff --git a/examples/powertools-examples-serialization/pom.xml b/examples/powertools-examples-serialization/pom.xml index e54c84315..4f2b8ffda 100644 --- a/examples/powertools-examples-serialization/pom.xml +++ b/examples/powertools-examples-serialization/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-serialization jar Powertools for AWS Lambda (Java) library Examples - Serialization diff --git a/examples/powertools-examples-sqs/pom.xml b/examples/powertools-examples-sqs/pom.xml index 9d0b3f2a8..a6737bfed 100644 --- a/examples/powertools-examples-sqs/pom.xml +++ b/examples/powertools-examples-sqs/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-sqs jar Powertools for AWS Lambda (Java) library Examples - SQS diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 3cb4264c0..42d484ed1 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 1.18.0 + 1.19.0-SNAPSHOT powertools-examples-validation jar Powertools for AWS Lambda (Java) library Examples - Validation diff --git a/pom.xml b/pom.xml index 76b59d747..0ac62cbda 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-parent - 1.18.0 + 1.19.0-SNAPSHOT pom Powertools for AWS Lambda (Java) library Parent diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index cedbcf317..fabaeba30 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 1.18.0 + 1.19.0-SNAPSHOT A suite of utilities that makes batch message processing using AWS Lambda easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 660f6e34a..54c104468 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java)library Cloudformation diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index 0d82530a0..041950cf5 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) library Core diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 995aeb1c7..502b14eb3 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -10,7 +10,7 @@ Fake handlers that use Powertools for AWS Lambda (Java). - 1.18.0 + 1.19.0-SNAPSHOT UTF-8 1.8 1.8 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index d2aeaed66..05c544641 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT powertools-e2e-tests diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index fe85d74fd..94aceebfc 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-parent - 1.18.0 + 1.19.0-SNAPSHOT powertools-idempotency diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 2d5540c54..04e46dc6e 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ software.amazon.lambda powertools-parent - 1.18.0 + 1.19.0-SNAPSHOT powertools-large-messages diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 6fd4ce89f..78c36f41a 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) library Logging diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 677e18ed7..53df4cf57 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) library Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 0e86a0a06..788ac9438 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT powertools-parameters diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 06b828923..70280a0b8 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT powertools-serialization diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index e14ec59b5..d65458d5e 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) library SQS diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index a6e84c4ac..50e473ad1 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) library Test Suite diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index f16bedd2f..26bc0aa16 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) library Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index a372e4740..11ad1398f 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 1.18.0 + 1.19.0-SNAPSHOT Powertools for AWS Lambda (Java) validation library From 9d8b614337e3c6032965b084cc537633b7ec38e2 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Fri, 17 Nov 2023 13:46:53 +0100 Subject: [PATCH 52/59] Add some more margin to the test pause (#1518) --- .../amazon/lambda/powertools/LargeMessageIdempotentE2ET.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"); From 223d1969e128561945fdba1e268a4ad8eaacf9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:00:23 +0100 Subject: [PATCH 53/59] test: e2e tests with java 21 (#1517) * e2e tests with java 21 * Run Java21 tests using the Java17 compiler * Run all of the E2E tests in parallel, not just the first 3 * Try again * . * Let's try again * Add some comment on Java21 to the repo * Add caveat about lambda runtimes * Clean up wording a little --------- Co-authored-by: Scott Gerring --- .github/workflows/run-e2e-tests.yml | 15 ++++++--- README.md | 8 +++++ docs/index.md | 10 +++++- powertools-e2e-tests/pom.xml | 2 +- .../powertools/testutils/Infrastructure.java | 32 ++++++++++++------- .../powertools/testutils/JavaRuntime.java | 3 +- 6 files changed, 51 insertions(+), 19 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index a2a1b9aec..9e71f56af 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -32,13 +32,19 @@ 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 + + # If matrix.version is 21, use 17, otherwise use matrix.version + # This is because AspectJ does not yet support weaving with Java21; we want + # to test the Java21 runtime, but we can't yet use the JDK21 compiler. + # https://github.com/eclipse-aspectj/aspectj/issues/260#issuecomment-1815920274 + JAVA_VERSION: ${{ (matrix.java == 21 && '17') || matrix.java }} + JAVA_LAMBDA_RUNTIME_VERSION: ${{ matrix.java }} permissions: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read @@ -48,7 +54,8 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - java-version: ${{ matrix.java }} + # See comment above on JAVA_VERSION env var + java-version: ${{ (matrix.java == 21 && '17') || matrix.java }} cache: maven - name: Setup AWS credentials uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 diff --git a/README.md b/README.md index 63ba35cc6..bdf779bfb 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,14 @@ 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/)** +### 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). + +AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). +If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 +Lambda runtimes. + ### Installation Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it diff --git a/docs/index.md b/docs/index.md index 92589be7c..b1b55e2d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,10 +11,18 @@ 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. +???+ tip "Java Compatability" + 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). + + AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). + If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 + Lambda runtimes. + ## Tenets This project separates core utilities that will be available in other runtimes vs general utilities that might not be available across all runtimes. diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 05c544641..8a0b65f32 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ 1.8 1.8 10.3.0 - 2.100.0 + 2.109.0 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..11ee24b05 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 @@ -114,6 +114,7 @@ public class Infrastructure { private final String queue; private final String kinesisStream; private final String largeMessagesBucket; + private final JavaRuntime lambdaRuntimeVersion; private String ddbStreamsTableName; private String functionName; private Object cfnTemplate; @@ -124,6 +125,7 @@ private Infrastructure(Builder builder) { this.tracing = builder.tracing; this.envVar = builder.environmentVariables; this.runtime = builder.runtime; + this.lambdaRuntimeVersion = builder.lambdaRuntimeVersion; this.timeout = builder.timeoutInSeconds; this.pathToFunction = builder.pathToFunction; this.idempotencyTable = builder.idemPotencyTable; @@ -204,6 +206,7 @@ public void destroy() { private Stack createStackWithLambda() { boolean createTableForAsyncTests = false; Stack stack = new Stack(app, stackName); + List packagingInstruction = Arrays.asList( "/bin/sh", "-c", @@ -247,7 +250,7 @@ private Stack createStackWithLambda() { .handler("software.amazon.lambda.powertools.e2e.Function::handleRequest") .memorySize(1024) .timeout(Duration.seconds(timeout)) - .runtime(runtime.getCdkRuntime()) + .runtime(lambdaRuntimeVersion.getCdkRuntime()) .environment(envVar) .tracing(tracing ? Tracing.ACTIVE : Tracing.DISABLED) .build(); @@ -504,29 +507,34 @@ public static class Builder { private String queue; private String kinesisStream; private String ddbStreamsTableName; + private JavaRuntime lambdaRuntimeVersion; private Builder() { - getJavaRuntime(); + runtime = mapRuntimeVersion("JAVA_VERSION"); + lambdaRuntimeVersion = mapRuntimeVersion("JAVA_LAMBDA_RUNTIME_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("JAVA_LAMBDA_RUNTIME_VERSION 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; From eeaec59b777dfab1f513366c7d270c7866ce097c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Van=20Der=20Linden?= <117538+jeromevdl@users.noreply.github.com> Date: Mon, 20 Nov 2023 07:46:09 +0100 Subject: [PATCH 54/59] update doc for ALC (#1520) --- docs/core/logging.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/core/logging.md b/docs/core/logging.md index 0391ae7b4..70781b1b2 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -596,12 +596,18 @@ via `samplingRate` attribute on annotation. POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 ``` -## AWS Lambda Advanced Logging Controls -With AWS [Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced), you can control the output format of your logs as either `TEXT` or `JSON` and specify the minimum accepted log level for your application. -Regardless of the output format setting in Lambda, Powertools for AWS Lambda will always output JSON formatted logging messages. +## AWS Lambda Advanced Logging Controls (ALC) -When you have this feature enabled, log messages that don’t meet the configured log level are discarded by Lambda. -For example, if you set the minimum log level to `WARN`, you will only receive `WARN` and `ERROR` messages in your AWS CloudWatch Logs, all other log levels will be discarded by Lambda. +!!!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 @@ -610,29 +616,29 @@ sequenceDiagram 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.warn("Something happened") + 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->>Lambda service: DROP INFO and DEBUG logs - + Lambda service--xLambda service: DROP INFO and DEBUG logs Lambda service->>CloudWatch Logs: Ingest error logs ``` -Logger will automatically listen for the `AWS_LAMBDA_LOG_FORMAT` and `AWS_LAMBDA_LOG_LEVEL` environment variables, and change behaviour if they’re found to ensure as much compatibility as possible. - ### Priority of log level settings in Powertools for AWS Lambda -When the Advanced Logging Controls feature is 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) for more details. - We prioritise log level settings in this order: 1. `AWS_LAMBDA_LOG_LEVEL` environment variable 2. `POWERTOOLS_LOG_LEVEL` environment variable -In the event you have set `POWERTOOLS_LOG_LEVEL` to a level lower than the ACL setting, Powertools for AWS Lambda will output a warning log message informing you that your messages will be discarded by Lambda. +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 From 150b28ffbd9cc7fdba204eea2b63a93efd9b4204 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Mon, 20 Nov 2023 10:19:30 +0100 Subject: [PATCH 55/59] chore: Testing java21 aspectj pre-release (#1519) * e2e tests with java 21 * use aspectj 1.9.21-SNAPSHOT * Fix log4j2.xml missing in logging test for java21 * rollback double runtime * remove comment * keep aspectj 1.9.7 in parent for java8 compatibility * use M1 instead of snapshot * update documentation for aspectj * update documentation for aspectj --------- Co-authored-by: Jerome Van Der Linden --- README.md | 39 ++++-- docs/index.md | 43 ++++-- pom.xml | 3 +- powertools-e2e-tests/handlers/logging/pom.xml | 4 + powertools-e2e-tests/handlers/pom.xml | 126 +++++++----------- .../powertools/testutils/Infrastructure.java | 9 +- 6 files changed, 122 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index bdf779bfb..dbe8eb2fe 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,11 @@ 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/)** -### 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). - -AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). -If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 -Lambda runtimes. - -### 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 ... @@ -196,6 +188,33 @@ 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.18.0/examples)** for example projects showcasing usage of different utilities. diff --git a/docs/index.md b/docs/index.md index b1b55e2d6..884e02476 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,14 +15,6 @@ Powertools for AWS Lambda (Java) is a suite of utilities for AWS Lambda Function 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. -???+ tip "Java Compatability" - 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). - - AspectJ does not yet support Java 21 [[1]](https://github.com/eclipse-aspectj/aspectj/issues/260), [[2]](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md). - If you need to use aspects - either Powertools features leveraging aspects or other libraries - you should use the JDK 17 compiler and target either the Java 17 or Java 21 - Lambda runtimes. - ## Tenets This project separates core utilities that will be available in other runtimes vs general utilities that might not be available across all runtimes. @@ -282,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 diff --git a/pom.xml b/pom.xml index 0ac62cbda..3c30dc260 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,6 @@ 1.8 2.20.0 2.15.3 - 1.9.7 2.21.0 2.14.0 2.1.3 @@ -84,6 +83,7 @@ 3.11.3 1.1.2 3.11.0 + 1.9.7 1.13.1 3.1.2 0.8.10 @@ -516,6 +516,7 @@ + jdk16 diff --git a/powertools-e2e-tests/handlers/logging/pom.xml b/powertools-e2e-tests/handlers/logging/pom.xml index 4b613f2bf..61ec6b414 100644 --- a/powertools-e2e-tests/handlers/logging/pom.xml +++ b/powertools-e2e-tests/handlers/logging/pom.xml @@ -17,6 +17,10 @@ software.amazon.lambda powertools-logging + + org.aspectj + aspectjrt + com.amazonaws aws-lambda-java-events diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 502b14eb3..a9096477b 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -42,7 +42,11 @@ pom import - + + org.aspectj + aspectjrt + ${aspectj.version} + software.amazon.lambda powertools-logging @@ -143,96 +147,66 @@ false + + dev.aspectj + aspectj-maven-plugin + ${aspectj.plugin.version} + + true + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.target} + ignore + ${project.build.sourceEncoding} + + + + + compile + test-compile + + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + - jdk8 + jdk8to16 - (,11) + [1.8,16] 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} - ignore - ${project.build.sourceEncoding} - - - - process-sources - - compile - test-compile - - - - - - - org.aspectj - aspectjtools - ${aspectj.version} - - - - - - - jdk11plus + jdk17to20 - [11,) + [17,20] - - - - - dev.aspectj - aspectj-maven-plugin - ${aspectj.plugin.version} - - ${maven.compiler.source} - ${maven.compiler.target} - ${maven.compiler.target} - ignore - ${project.build.sourceEncoding} - - - - process-sources - - compile - test-compile - - - - - - - + + 1.9.20.1 + + + + jdk21 + + [21,) + + + 1.9.21.M1 + 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 11ee24b05..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 @@ -114,7 +114,6 @@ public class Infrastructure { private final String queue; private final String kinesisStream; private final String largeMessagesBucket; - private final JavaRuntime lambdaRuntimeVersion; private String ddbStreamsTableName; private String functionName; private Object cfnTemplate; @@ -125,7 +124,6 @@ private Infrastructure(Builder builder) { this.tracing = builder.tracing; this.envVar = builder.environmentVariables; this.runtime = builder.runtime; - this.lambdaRuntimeVersion = builder.lambdaRuntimeVersion; this.timeout = builder.timeoutInSeconds; this.pathToFunction = builder.pathToFunction; this.idempotencyTable = builder.idemPotencyTable; @@ -213,7 +211,6 @@ private Stack createStackWithLambda() { "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/" @@ -250,7 +247,7 @@ private Stack createStackWithLambda() { .handler("software.amazon.lambda.powertools.e2e.Function::handleRequest") .memorySize(1024) .timeout(Duration.seconds(timeout)) - .runtime(lambdaRuntimeVersion.getCdkRuntime()) + .runtime(runtime.getCdkRuntime()) .environment(envVar) .tracing(tracing ? Tracing.ACTIVE : Tracing.DISABLED) .build(); @@ -507,11 +504,9 @@ public static class Builder { private String queue; private String kinesisStream; private String ddbStreamsTableName; - private JavaRuntime lambdaRuntimeVersion; private Builder() { runtime = mapRuntimeVersion("JAVA_VERSION"); - lambdaRuntimeVersion = mapRuntimeVersion("JAVA_LAMBDA_RUNTIME_VERSION"); } @@ -520,7 +515,7 @@ 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_LAMBDA_RUNTIME_VERSION is not set"); + throw new IllegalArgumentException(environmentVariableName + " is not set"); } if (javaVersion.startsWith("8")) { ret = JavaRuntime.JAVA8AL2; From 2411a079cc6798cb050a0aebbacbf375a2ca6b37 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Wed, 22 Nov 2023 09:07:00 +0100 Subject: [PATCH 56/59] chore: Remove build cruft --- .github/workflows/run-e2e-tests.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 9e71f56af..626268214 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -38,13 +38,7 @@ jobs: name: End-to-end tests java${{ matrix.java }} env: AWS_DEFAULT_REGION: eu-west-1 - - # If matrix.version is 21, use 17, otherwise use matrix.version - # This is because AspectJ does not yet support weaving with Java21; we want - # to test the Java21 runtime, but we can't yet use the JDK21 compiler. - # https://github.com/eclipse-aspectj/aspectj/issues/260#issuecomment-1815920274 - JAVA_VERSION: ${{ (matrix.java == 21 && '17') || matrix.java }} - JAVA_LAMBDA_RUNTIME_VERSION: ${{ matrix.java }} + JAVA_VERSION: ${{ matrix.java }} permissions: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read @@ -54,8 +48,7 @@ jobs: uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: distribution: 'corretto' - # See comment above on JAVA_VERSION env var - java-version: ${{ (matrix.java == 21 && '17') || matrix.java }} + java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 From 772b30e8247f316f7c1c967b8135cece1f1901b0 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 29 Nov 2023 11:13:47 +0000 Subject: [PATCH 57/59] Adding context for using RequestStreamHandler --- .../sam/src/main/java/helloworld/AppStream.java | 3 +++ 1 file changed, 3 insertions(+) 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 index 0158b7e06..de874f12e 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java @@ -41,6 +41,9 @@ public class AppStream implements RequestStreamHandler { @Override @Logging(logEvent = true) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) + // RequestStreamHandler used from the AWS Lambda runtime for handling streaming events as it can process the input + // and output streams directly. Preferred when output data is represented as a stream of bytes, + // rather than a simple HTTP request-response model. public void handleRequest(InputStream input, OutputStream output, Context context) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) { From 2259c66d8fb3474f57899554cca96a62efb479e1 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 29 Nov 2023 11:24:43 +0000 Subject: [PATCH 58/59] removing pr_lint --- .github/workflows/pr_iac_lint_.yml | 45 ------------------------------ 1 file changed, 45 deletions(-) delete mode 100644 .github/workflows/pr_iac_lint_.yml diff --git a/.github/workflows/pr_iac_lint_.yml b/.github/workflows/pr_iac_lint_.yml deleted file mode 100644 index 209072b02..000000000 --- a/.github/workflows/pr_iac_lint_.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Validate IaC - -on: - pull_request: - branches: - - main - - v2 - paths: - - 'examples/**' -jobs: - linter: - runs-on: ubuntu-latest - strategy: - matrix: - project: ["sam", "gradle", "kotlin"] - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - name: Setup java JDK - uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 - with: - distribution: 'corretto' - java-version: 11 - - name: Run SAM validator to check syntax of IaC templates - Java - working-directory: examples/powertools-examples-core/${{ matrix.project }} - run: | - sam build - sam validate --lint - - name: Setup Terraform - uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 #v2.0.3 - - name: Run Terraform validator to check syntax of IaC templates and produce a plan of changes - working-directory: examples/powertools-examples-core/terraform - run: | - mvn install - terraform -version - terraform init -backend=false - terraform validate - terraform plan - - name: Setup Terraform lint - uses: terraform-linters/setup-tflint@a5a1af8c6551fb10c53f1cd4ba62359f1973746f # v3.1.1 - - name: Run Terraform lint to check for best practices, errors, deprecated syntax etc. - working-directory: examples/powertools-examples-core/terraform - run: | - tflint --version - tflint --init - tflint -f compact From 080edc0b9e5e9eb87659a4e85353bd8e562262bd Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Mon, 4 Dec 2023 13:47:43 +0000 Subject: [PATCH 59/59] Update examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java Clarify usage of RequestStreamHandler. Co-authored-by: Alexey Soshin --- .../sam/src/main/java/helloworld/AppStream.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 index de874f12e..94806cc38 100644 --- a/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/sam/src/main/java/helloworld/AppStream.java @@ -41,9 +41,9 @@ public class AppStream implements RequestStreamHandler { @Override @Logging(logEvent = true) @Metrics(namespace = "ServerlessAirline", service = "payment", captureColdStart = true) - // RequestStreamHandler used from the AWS Lambda runtime for handling streaming events as it can process the input - // and output streams directly. Preferred when output data is represented as a stream of bytes, - // rather than a simple HTTP request-response model. + // RequestStreamHandler can be used instead of RequestHandler for cases when you'd like to deserialize request body or serialize response body yourself, instead of allowing that to happen automatically + // Note that you still need to return a proper JSON for API Gateway to handle + // See Lambda Response format for examples: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html public void handleRequest(InputStream input, OutputStream output, Context context) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)))) {