From 162e343670baab48841b5c1d75de86847ae1b0a3 Mon Sep 17 00:00:00 2001 From: Harry Nguyen Date: Fri, 23 Oct 2026 13:39:24 -0700 Subject: [PATCH 1/2] feat(telemetry): add Durable Execution Telemetry conformance test handlers --- conformance-tests-telemetry/README.md | 75 +++++++++++++++ conformance-tests-telemetry/pom.xml | 94 +++++++++++++++++++ .../telemetry/Telemetry1Success.java | 74 +++++++++++++++ conformance-tests-telemetry/template.yaml | 46 +++++++++ 4 files changed, 289 insertions(+) create mode 100644 conformance-tests-telemetry/README.md create mode 100644 conformance-tests-telemetry/pom.xml create mode 100644 conformance-tests-telemetry/src/main/java/software/amazon/lambda/durable/conformance/telemetry/Telemetry1Success.java create mode 100644 conformance-tests-telemetry/template.yaml diff --git a/conformance-tests-telemetry/README.md b/conformance-tests-telemetry/README.md new file mode 100644 index 000000000..74c8fcf1e --- /dev/null +++ b/conformance-tests-telemetry/README.md @@ -0,0 +1,75 @@ +# Durable Execution Java SDK — Durable Execution Telemetry Conformance Tests + +This module answers one question. Does the telemetry exporter (`aws-durable-execution-sdk-java-plugin-telemetry-exporter`), +with its `CloudWatchLogsExporter`, deliver a complete `DurableExecutionTelemetry` record to CloudWatch Logs for a real +Lambda execution? + +Unit tests can assert that the plugin builds a record. They cannot assert that the record reaches the configured log +group on a real invocation or that it carries the right identity fields for the deployed function. Those are properties +of a deployed function. So the scenario here is a Lambda function. The runner deploys it with the plugin configured, +invokes it once, fetches the record from CloudWatch Logs, and checks it against the requirement. + +The requirement and the runner live in +[`aws/aws-durable-execution-conformance-tests`](https://github.com/aws/aws-durable-execution-conformance-tests), package +`aws-durable-execution-conformance-tests-telemetry`. This module holds the SDK-specific part: the handler and the SAM +template that maps it to the requirement ID. + +## Coverage + +| Suite | Requirement | What it pins down | +| ----------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `telemetry` | `telemetry-1` | One successful single-step execution emits one `SUCCEEDED` record with `input`, `output`, ARN-derived identity fields, and `operationsByName`. | + +## Layout + +``` +pom.xml # standalone Maven project (not a reactor module) +template.yaml # telemetry suite; declares TelemetryLogGroupName and creates the group +src/main/java/software/amazon/lambda/durable/conformance/telemetry/ + Telemetry1Success.java # single step "increment"; plugin + CloudWatchLogsExporter +``` + +The handler uses the real SDK API only. It constructs no record and calls no exporter method. A missing or wrong record +is the plugin's defect. That defect is the signal the suite measures. + +The handler configuration is: one `CloudWatchLogsExporter`, emit mode `ON_COMPLETE`, sampling rate 1, operation detail +`TOP_LEVEL`, and content input and output enabled. + +## Template contract + +The runner passes `TelemetryLogGroupName`. Extensions have no pre-deploy hook, so the template creates the group with an +`AWS::Logs::LogGroup` resource, and the function depends on it. The group name reaches the handler through +`TELEMETRY_LOG_GROUP_NAME`. The handler fails at class initialization when that variable is unset. + +The `LambdaExecutionRoleArn` role must allow `logs:CreateLogStream` and `logs:PutLogEvents` on the group. +`AWSLambdaBasicDurableExecutionRolePolicy` grants both on `*`. + +## Building locally + +Prerequisites: JDK 17 or later, Maven, and the AWS SAM CLI. + +```bash +# from the repository root: install the SDK and the telemetry exporter to the local Maven repository +mvn -B -q -Dmaven.test.skip=true install + +# tell this module which SDK version to build against (the root pom version) +export JAVA_SDK_VERSION=$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout) + +cd conformance-tests-telemetry +sam build +``` + +The pom reads the SDK version from `JAVA_SDK_VERSION` through the `durable.sdk.version` property. The exporter declares +`cloudwatchlogs` as optional, so this module declares it directly. + +Cloud run, from a checkout of the conformance repository with the telemetry package installed: + +```bash +durable-execution-conformance \ + --template /conformance-tests-telemetry/template.yaml \ + --language java --region us-west-2 --suite telemetry \ + --report console json \ + --parameter-overrides LambdaExecutionRoleArn= +``` + +A caller GitHub workflow is a follow-on. diff --git a/conformance-tests-telemetry/pom.xml b/conformance-tests-telemetry/pom.xml new file mode 100644 index 000000000..4c55da4c0 --- /dev/null +++ b/conformance-tests-telemetry/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + software.amazon.lambda.durable + aws-durable-execution-sdk-java-conformance-tests-telemetry + 1.0.0 + jar + + AWS Lambda Durable Execution SDK Telemetry Conformance Tests + + Java SDK handlers and SAM template for the Durable Execution Telemetry conformance + suite (telemetry-1). + + + + UTF-8 + 17 + 17 + 17 + + ${env.JAVA_SDK_VERSION} + 2.56.0 + 1.4.1 + + + + + software.amazon.lambda.durable + aws-durable-execution-sdk-java + ${durable.sdk.version} + + + software.amazon.lambda.durable + aws-durable-execution-sdk-java-plugin-telemetry-exporter + ${durable.sdk.version} + + + software.amazon.awssdk + cloudwatchlogs + ${aws.sdk.version} + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.core.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.2 + + false + + + + package + + shade + + + + + + + + + + + + diff --git a/conformance-tests-telemetry/src/main/java/software/amazon/lambda/durable/conformance/telemetry/Telemetry1Success.java b/conformance-tests-telemetry/src/main/java/software/amazon/lambda/durable/conformance/telemetry/Telemetry1Success.java new file mode 100644 index 000000000..ffd63802e --- /dev/null +++ b/conformance-tests-telemetry/src/main/java/software/amazon/lambda/durable/conformance/telemetry/Telemetry1Success.java @@ -0,0 +1,74 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.amazon.lambda.durable.conformance.telemetry; + +import software.amazon.lambda.durable.DurableConfig; +import software.amazon.lambda.durable.DurableContext; +import software.amazon.lambda.durable.DurableHandler; +import software.amazon.lambda.durable.telemetry.ContentConfig; +import software.amazon.lambda.durable.telemetry.DurableExecutionTelemetry; +import software.amazon.lambda.durable.telemetry.DurableExecutionTelemetryConfig; +import software.amazon.lambda.durable.telemetry.exporters.CloudWatchLogsExporter; + +/** + * Single successful step scenario for requirement telemetry-1. + * + *

The handler configures the telemetry plugin with one CloudWatch Logs exporter. It constructs no record and calls + * no exporter method. The plugin builds and exports the record. + */ +public final class Telemetry1Success extends DurableHandler { + + // The deployed function must export to the log group the runner reads. + // A missing variable would produce a function that exports nowhere. + // So class initialization fails when the variable is unset. + private static final String LOG_GROUP_NAME = System.getenv("TELEMETRY_LOG_GROUP_NAME"); + + static { + if (LOG_GROUP_NAME == null || LOG_GROUP_NAME.isBlank()) { + throw new IllegalStateException("TELEMETRY_LOG_GROUP_NAME must be set"); + } + } + + @Override + protected DurableConfig createConfiguration() { + return DurableConfig.builder() + .withPlugins(DurableExecutionTelemetry.durableExecutionTelemetry( + DurableExecutionTelemetryConfig.builder() + .addExporter(CloudWatchLogsExporter.builder() + .logGroupName(LOG_GROUP_NAME) + .build()) + .emitMode(DurableExecutionTelemetryConfig.EmitMode.ON_COMPLETE) + .samplingRate(1.0) + .operationDetail(DurableExecutionTelemetryConfig.OperationDetail.TOP_LEVEL) + .content(ContentConfig.builder() + .input(true) + .output(true) + .build()) + .build())) + .build(); + } + + @Override + public Output handleRequest(Input input, DurableContext context) { + int value = context.step("increment", Integer.class, step -> input.value + 1); + return new Output(value); + } + + /** Event payload: {"value": 41}. */ + public static final class Input { + public int value; + + public Input() {} + } + + /** Result payload: {"value": 42}. */ + public static final class Output { + public int value; + + public Output() {} + + public Output(int value) { + this.value = value; + } + } +} diff --git a/conformance-tests-telemetry/template.yaml b/conformance-tests-telemetry/template.yaml new file mode 100644 index 000000000..9bf259523 --- /dev/null +++ b/conformance-tests-telemetry/template.yaml @@ -0,0 +1,46 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: Java SDK examples for the Durable Execution Telemetry conformance suite + +Parameters: + LambdaExecutionRoleArn: + Type: String + Description: Pre-created execution role for the test functions + TelemetryLogGroupName: + Type: String + Description: CloudWatch Logs group that receives Durable Execution Telemetry records + +Globals: + Function: + Runtime: java21 + Architectures: + - x86_64 + Timeout: 60 + MemorySize: 1024 + DurableConfig: + ExecutionTimeout: 300 + RetentionPeriodInDays: 1 + Environment: + Variables: + TELEMETRY_LOG_GROUP_NAME: !Ref TelemetryLogGroupName + +Resources: + # Extensions have no pre-deploy hook, so CloudFormation creates the group + # before the runner invokes any function. + TelemetryLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: !Ref TelemetryLogGroupName + RetentionInDays: 1 + + Telemetry1Success: + Type: AWS::Serverless::Function + DependsOn: TelemetryLogGroup + TestingMetadata: + TestDescription: + - telemetry-1 + Properties: + CodeUri: . + Handler: software.amazon.lambda.durable.conformance.telemetry.Telemetry1Success + FunctionName: !Sub "${AWS::StackName}-telemetry-1" + Role: !Ref LambdaExecutionRoleArn From 4e3d3bb02f371fe3bdf1a0593eaff518c72da603 Mon Sep 17 00:00:00 2001 From: Harry Nguyen Date: Fri, 23 Oct 2026 18:02:02 -0700 Subject: [PATCH 2/2] fix(telemetry): drop explicit FunctionName from conformance template The otel and core conformance templates let CloudFormation name each function. The telemetry runner resolves FUNCTION_NAME from the invoked ARN and reads only the TelemetryLogGroupName parameter. So the explicit name serves no consumer. A named function also blocks any stack update that needs replacement. So the line is removed. --- conformance-tests-telemetry/template.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/conformance-tests-telemetry/template.yaml b/conformance-tests-telemetry/template.yaml index 9bf259523..1f900ba84 100644 --- a/conformance-tests-telemetry/template.yaml +++ b/conformance-tests-telemetry/template.yaml @@ -42,5 +42,4 @@ Resources: Properties: CodeUri: . Handler: software.amazon.lambda.durable.conformance.telemetry.Telemetry1Success - FunctionName: !Sub "${AWS::StackName}-telemetry-1" Role: !Ref LambdaExecutionRoleArn