diff --git a/.github/test-matrix.json b/.github/test-matrix.json
new file mode 100644
index 00000000..7e6539cb
--- /dev/null
+++ b/.github/test-matrix.json
@@ -0,0 +1,16 @@
+{
+ "arch": [
+ {
+ "runner": "ubuntu-latest",
+ "label": "x64",
+ "sam_arch": "x86_64",
+ "java_suffix": "X64"
+ },
+ {
+ "runner": "ubuntu-24.04-arm",
+ "label": "arm64",
+ "sam_arch": "arm64",
+ "java_suffix": "ARM64"
+ }
+ ]
+}
diff --git a/.github/workflows/build-integration-test.yml b/.github/workflows/build-integration-test.yml
new file mode 100644
index 00000000..2a6bb30c
--- /dev/null
+++ b/.github/workflows/build-integration-test.yml
@@ -0,0 +1,86 @@
+# this workflow verifies that the integration test Lambda function builds successfully.
+# it does NOT deploy or run the tests (that requires AWS credentials and is done in
+# run-integration-test.yml).
+
+name: Build integration tests
+
+on:
+ push:
+ branches: [ main ]
+ paths:
+ - 'aws-lambda-java-log4j2/**'
+ - 'aws-lambda-java-core/**'
+ - 'lambda-integration-tests/**'
+ pull_request:
+ branches: [ '*' ]
+ paths:
+ - 'aws-lambda-java-log4j2/**'
+ - 'aws-lambda-java-core/**'
+ - 'lambda-integration-tests/**'
+ - '.github/workflows/build-integration-test.yml'
+
+permissions:
+ contents: read
+
+jobs:
+ load-matrix:
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.set.outputs.matrix }}
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - name: Load test matrix
+ id: set
+ run: |
+ MATRIX=$(jq -c '.' .github/test-matrix.json)
+ echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT"
+
+ build-arch:
+ needs: load-matrix
+ runs-on: ${{ matrix.arch.runner }}
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJson(needs.load-matrix.outputs.matrix) }}
+ name: "build (${{ matrix.arch.label }})"
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - name: Set up JDK
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
+ with:
+ java-version: |
+ 8
+ 21
+ distribution: corretto
+ cache: maven
+
+ - name: Install core with Maven
+ run: |
+ export JAVA_HOME=$JAVA_HOME_8_${{ matrix.arch.java_suffix }}
+ mvn -B install --file aws-lambda-java-core/pom.xml
+
+ - name: Install log4j2 with Maven
+ run: |
+ export JAVA_HOME=$JAVA_HOME_8_${{ matrix.arch.java_suffix }}
+ mvn -B install --file aws-lambda-java-log4j2/pom.xml
+
+ # build the integration test function
+ # this verifies that the function compiles and packages correctly.
+ # the tests will run in run-integration-test.yml which deploys to AWS.
+ - name: Package integration test function
+ run: |
+ export JAVA_HOME=$JAVA_HOME_21_${{ matrix.arch.java_suffix }}
+ mvn -B package --file lambda-integration-tests/log4j2-test-function/pom.xml
+
+ build:
+ needs: build-arch
+ if: always()
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check build results
+ run: |
+ if [ "${{ needs.build-arch.result }}" != "success" ]; then
+ echo "Build failed on one or more architectures"
+ exit 1
+ fi
diff --git a/.github/workflows/run-integration-test.yml b/.github/workflows/run-integration-test.yml
new file mode 100644
index 00000000..1b857f45
--- /dev/null
+++ b/.github/workflows/run-integration-test.yml
@@ -0,0 +1,120 @@
+# this workflow deploys a Lambda function that uses aws-lambda-java-log4j2,
+# invokes it, and verifies that logs arrive in CloudWatch.
+
+name: Run integration tests
+
+permissions:
+ id-token: write
+ contents: read
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ main ]
+ paths:
+ - 'aws-lambda-java-log4j2/**'
+ - 'aws-lambda-java-core/**'
+ - 'lambda-integration-tests/**'
+
+jobs:
+ load-matrix:
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.set.outputs.matrix }}
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - name: Load test matrix
+ id: set
+ run: |
+ MATRIX=$(jq -c '.' .github/test-matrix.json)
+ echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT"
+
+ run-integration-tests:
+ needs: load-matrix
+ # Only run on the main repo, not forks
+ if: ${{ github.repository_owner == 'aws' }}
+ runs-on: ${{ matrix.arch.runner }}
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJson(needs.load-matrix.outputs.matrix) }}
+ name: "integration-test (${{ matrix.arch.label }})"
+ concurrency:
+ group: integration-test-${{ matrix.arch.label }}
+ cancel-in-progress: false
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - name: Set up JDK
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
+ with:
+ java-version: |
+ 8
+ 21
+ distribution: corretto
+ cache: maven
+
+ - name: Install SAM CLI
+ uses: aws-actions/setup-sam@f84ec7d548307efafe33230528756de3c5841a17 # v2
+ with:
+ use-installer: true
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
+ with:
+ role-to-assume: ${{ secrets.AWS_ROLE_LOG4J2_INTEG_TEST }}
+ role-session-name: GitHubActionsLog4j2IntegTest
+ aws-region: ${{ secrets.AWS_REGION_LOG4J2_INTEG_TEST }}
+
+ - name: Install core with Maven
+ run: |
+ export JAVA_HOME=$JAVA_HOME_8_${{ matrix.arch.java_suffix }}
+ mvn -B install --file aws-lambda-java-core/pom.xml
+
+ - name: Install log4j2 with Maven
+ run: |
+ export JAVA_HOME=$JAVA_HOME_8_${{ matrix.arch.java_suffix }}
+ mvn -B install --file aws-lambda-java-log4j2/pom.xml
+
+ - name: Build SAM stack
+ run: |
+ export JAVA_HOME=$JAVA_HOME_21_${{ matrix.arch.java_suffix }}
+ cd lambda-integration-tests && sam build
+
+ - name: Validate SAM stack
+ run: cd lambda-integration-tests && sam validate --lint
+
+ - name: Deploy stack
+ id: deploy_stack
+ env:
+ AWS_REGION: ${{ secrets.AWS_REGION_LOG4J2_INTEG_TEST }}
+ run: |
+ cd lambda-integration-tests
+ stackName="aws-lambda-java-log4j2-integ-test-${{ matrix.arch.label }}-$GITHUB_RUN_ID"
+ echo "STACK_NAME=$stackName" >> "$GITHUB_OUTPUT"
+ echo "Stack name = $stackName"
+ sam deploy \
+ --stack-name "${stackName}" \
+ --parameter-overrides "ParameterKey=LambdaRole,ParameterValue=${{ secrets.AWS_LAMBDA_ROLE_LOG4J2_INTEG_TEST }} ParameterKey=Architecture,ParameterValue=${{ matrix.arch.sam_arch }}" \
+ --no-confirm-changeset \
+ --no-progressbar \
+ --s3-bucket "${{ secrets.S3_BUCKET_LOG4J2_INTEG_TEST }}" \
+ --capabilities CAPABILITY_IAM \
+ 2>&1 | tee /tmp/sam-deploy.log | tail -n 20
+ LOG4J2_TEST_FUNCTION=$(sam list stack-outputs --stack-name "${stackName}" --output json | jq -r '.[] | select(.OutputKey=="Log4j2TestFunction") | .OutputValue')
+ echo "LOG4J2_TEST_FUNCTION=$LOG4J2_TEST_FUNCTION" >> "$GITHUB_OUTPUT"
+ echo "Function name: $LOG4J2_TEST_FUNCTION"
+
+ - name: Run integration test
+ env:
+ LOG4J2_TEST_FUNCTION: ${{ steps.deploy_stack.outputs.LOG4J2_TEST_FUNCTION }}
+ AWS_REGION: ${{ secrets.AWS_REGION_LOG4J2_INTEG_TEST }}
+ run: ./lambda-integration-tests/run-tests.sh
+
+ - name: Cleanup
+ if: always() && steps.deploy_stack.outputs.STACK_NAME
+ env:
+ AWS_REGION: ${{ secrets.AWS_REGION_LOG4J2_INTEG_TEST }}
+ STACK_NAME: ${{ steps.deploy_stack.outputs.STACK_NAME }}
+ run: |
+ sam delete --stack-name "${STACK_NAME}" --no-prompts --region "${AWS_REGION}"
diff --git a/lambda-integration-tests/log4j2-test-function/pom.xml b/lambda-integration-tests/log4j2-test-function/pom.xml
new file mode 100644
index 00000000..b036d1de
--- /dev/null
+++ b/lambda-integration-tests/log4j2-test-function/pom.xml
@@ -0,0 +1,77 @@
+
+ 4.0.0
+
+ com.amazonaws
+ log4j2-integration-test-function
+ 1.0.0
+ jar
+
+ Log4j2 Integration Test Function
+
+ Lambda function used to verify that aws-lambda-java-log4j2 correctly emits logs to CloudWatch.
+
+
+
+ 21
+ 21
+ UTF-8
+ 2.25.4
+
+
+
+
+ com.amazonaws
+ aws-lambda-java-core
+ 1.4.0
+
+
+ com.amazonaws
+ aws-lambda-java-log4j2
+ 1.6.4
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.1
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
+
+ com.github.edwgiz
+ maven-shade-plugin.log4j2-cachefile-transformer
+ 2.8.1
+
+
+
+
+
+
diff --git a/lambda-integration-tests/log4j2-test-function/src/main/java/integ/Log4j2TestHandler.java b/lambda-integration-tests/log4j2-test-function/src/main/java/integ/Log4j2TestHandler.java
new file mode 100644
index 00000000..d81a3fa2
--- /dev/null
+++ b/lambda-integration-tests/log4j2-test-function/src/main/java/integ/Log4j2TestHandler.java
@@ -0,0 +1,30 @@
+package integ;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+
+/**
+ * integration test handler that logs a marker string using Log4j2 with the LambdaAppender.
+ * the test verifies that the marker appears in CloudWatch Logs, confirming end-to-end
+ * log delivery through the aws-lambda-java-log4j2 library.
+ */
+public class Log4j2TestHandler implements RequestHandler