Skip to content

docs(observability): add Durable Execution Telemetry pages - #296

Open
ParidelPooya wants to merge 7 commits into
mainfrom
docs/durable-execution-telemetry
Open

ParidelPooya wants to merge 7 commits into
mainfrom
docs/durable-execution-telemetry

Conversation

@ParidelPooya

Copy link
Copy Markdown

Summary

This PR adds a Durable Execution Telemetry section under SDK Reference → Observability. The section has three pages:

  • Durable Execution Telemetry (telemetry/index.md):
    • what a telemetry record is
    • how it differs from logs and OpenTelemetry traces
    • the record schema
    • the operations and operationsByName shapes
    • one short paragraph that describes the VS Code and desktop explorers as experimental previews
  • Exporter (telemetry/exporter.md):
    • install
    • register the plugin, and the method signature
    • the 13 exporters, with a multiple-exporter example
    • emit mode, sampling, operation detail, and content transforms
    • truncation
    • custom exporters
    • STOPPED and TIMED_OUT executions
    • naming operations
  • MCP Server (telemetry/mcp.md):
    • client setup for Kiro, Claude Code, and JSON configuration
    • environment configuration for each destination
    • the five tools and two prompts
    • result bounding
    • read-only access and data handling

It also adds the three pages to the navigation in zensical.toml. It adds links to the new pages from plugins.md and from the OpenTelemetry page's "See also" list.

The pages have tabs for TypeScript, Python, Java, and Go. The exporter is not available for C#, so the pages state that and have no C# examples. The OpenTelemetry page handles Go and C# the same way.

Examples

Each language has three examples under examples/<lang>/sdk-reference/observability/telemetry/: basic-usage, multiple-exporters, and redact-content. All four languages use the same handler.

I checked every example against the merged SDK code:

  • TypeScript: tsc --strict passes. basic-usage and redact-content ran under LocalDurableTestRunner. basic-usage wrote exactly one DurableExecutionTelemetry record through the default exporter.
  • Python: ruff format and ruff check pass. All three examples ran under DurableFunctionTestRunner. The AWS clients were mocked for multiple-exporters.
  • Java: the examples compile against 3.0.0-SNAPSHOT. basic-usage and the transforms in redact-content ran under LocalDurableTestRunner.
  • Go: go build, go vet, and gofmt pass, with the module built from the repository's main branch. basic-usage and redact-content ran under durabletest.

Checks

These pass locally:

  • mdformat --check docs/
  • codespell docs/
  • the hidden-character check
  • zensical build --clean, which reports "No issues found"

Merge timing

This site deploys on every push to main. The packages these pages tell readers to install are not all published yet:

Package Published?
npm @aws/durable-execution-sdk-js-telemetry-exporter No
npm @aws/durable-execution-sdk-js-telemetry-mcp No
PyPI aws-durable-execution-sdk-python-telemetry-exporter Yes, 0.0.1
Maven aws-durable-execution-sdk-java-plugin-telemetry-exporter No (the examples target 3.0.0)
Go module github.com/aws/aws-durable-execution-sdk-go/telemetry No (not tagged yet)

Please merge this PR after those releases go out.

@ParidelPooya
ParidelPooya deployed to ai-pr-review-runtime October 21, 2026 17:26 — with GitHub Actions Active
Comment on lines +59 to +60
`SUCCEEDED`, or `FAILED`. A suspended or retrying execution reports `RUNNING`. `endTime`
and `durationMs` appear only once the execution finishes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Not in on-change mode. TypeScript and Java also set endTime and durationMs on the RUNNING record sent at the end of each invocation. I'd fix the SDKs. Until then, this sentence needs the exception.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Added the exception: in on-change mode, TypeScript and Java also set endTime and durationMs on the RUNNING record they send at the end of each invocation.

Comment on lines +67 to +68
Destinations with a time type store the value in that type. Aurora and Redshift use
`TIMESTAMPTZ` columns, OpenSearch maps the fields as `date`, and the Athena table that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Only on Aurora PostgreSQL. With engine: "mysql", the exporter writes plain strings.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Now says Aurora PostgreSQL uses TIMESTAMPTZ, and that with engine: "mysql" the Aurora exporter writes plain strings.

Comment on lines +87 to +91
Error names are not the same across languages. Each SDK reports its own type names, so
the same failure can be `StepError` in TypeScript,
`aws_durable_execution_sdk_python.exceptions.StepError` in Python, and
`StepFailedException` in Java. Query `error.name` per SDK, or query on `message` and the
`cause` chain.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker: I ran a failing step, with no retries and no catch, in TypeScript, Python, and Java. Java never reported StepFailedException. Python and Java name the execution error and the operation error differently. This wording matches what each SDK wrote:

Suggested change
Error names are not the same across languages. Each SDK reports its own type names, so
the same failure can be `StepError` in TypeScript,
`aws_durable_execution_sdk_python.exceptions.StepError` in Python, and
`StepFailedException` in Java. Query `error.name` per SDK, or query on `message` and the
`cause` chain.
Error names are not the same across SDKs. For a step that throws `PaymentError`,
TypeScript reports `StepError` for both the execution and the operation, with
`PaymentError` as the `cause`. Python reports
`aws_durable_execution_sdk_python.exceptions.StepError` for the execution and the thrown
type's qualified name, such as `orders.PaymentError`, for the operation. Java reports
the thrown class's simple name, `PaymentError`, for the execution and its fully
qualified name, such as `com.example.PaymentError`, for the operation. So query the
execution `error.name` and `operations[].error.name` separately for each SDK, or query
on `message`.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Applied your wording.

Comment on lines +98 to +99
Destinations that cannot filter an array element by name store an `operationsByName`
map instead of the `operations` array. The exporters for CloudWatch Logs and DynamoDB use

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker: Not true for Go. Its CloudWatch Logs, Lambda log, and DynamoDB exporters write the operations array, and its DynamoDB item stores the record as a recordJson string. Please say so in the Go tab.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. The shared paragraph now scopes operationsByName to TypeScript, Python, and Java. The Go tab says Go's CloudWatch Logs, Lambda log, and DynamoDB exporters write the operations array, and that the DynamoDB item stores the record as a JSON string in recordJson.

Comment on lines +100 to +102
this shape. Each key is an operation name. Each value aggregates every occurrence of
that name with `count`, `failedCount`, `minDurationMs`, `maxDurationMs`,
`totalDurationMs`, `maxAttempt`, and the latest `status`. A query can then reach an

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Each value also has the latest type and subType. result and error appear only when the name ran once.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Added the latest type and subType, and that result and error appear only when the name ran once.

```json
{
"source": ["aws.lambda"],
"detail-type": ["Lambda Durable Execution State Change"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker: Lambda's detail type is Durable Execution Status Change (Monitoring durable functions), so this rule matches nothing.

Suggested change
"detail-type": ["Lambda Durable Execution State Change"],
"detail-type": ["Durable Execution Status Change"],

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Applied, and the lead-in now says "status changes".

Comment on lines +5 to +7
[exporter](exporter.md) writes. It is a local server that speaks the
[Model Context Protocol](https://modelcontextprotocol.io) (MCP) over stdio, so any
MCP-capable agent, such as Kiro, Claude Code, or Cursor, can use it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker: The tools read the TypeScript, Python, and Java record shape only. Go writes startTimestamp, so since and until filters skip Go records. Go's Lambda log line has no level/message envelope, so list_executions on lambda-log-exporter returns none. Please say which SDKs the tools support.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Added a paragraph after the intro: the tools read the TypeScript, Python, and Java record shape, do not support Go records yet, and why (startTimestamp, and no level/message envelope).

Comment on lines +53 to +54
`--scope project` writes `.mcp.json` at the repository root. The default user scope
writes `~/.claude.json`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The default scope is local, not user (MCP installation scopes).

Suggested change
`--scope project` writes `.mcp.json` at the repository root. The default user scope
writes `~/.claude.json`.
`--scope project` writes `.mcp.json` at the repository root. The default scope,
`local`, stores the server in `~/.claude.json` for this project only.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Applied.

Comment on lines +152 to +160
Every result holds at most 1,000 rows. A smaller `limit` returns fewer rows. A result
with `truncated: true` means the store may hold more matching data, so narrow the
filters instead of assuming the result is complete.

CloudWatch Logs Insights queries always cover a time window. `list_executions` derives
the window from `since`. `query` covers the last 24 hours and `get_execution` the last 7
days unless you pass `lookbackHours`. Each tool reports the window it searched in
`searchedLookbackHours`. So on a log destination, `found: false` from `get_execution`
means the record is not in that window.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Three corrections:

  • A Logs Insights query with its own limit or a stats gets no | limit 1000, so it can return more than 1,000 rows.
  • query does not return searchedLookbackHours. Only list_executions and get_execution do.
  • get_execution also returns found: false when the Logs Insights call fails, for example with AccessDenied.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Fixed all three: the | limit 1000 rule for query, searchedLookbackHours only on list_executions and get_execution, and found: false on a failed Logs Insights call.

public Map<String, String> handleRequest(Map<String, String> event, DurableContext context) {
String order = context.step("fetch-order", String.class, ctx -> event.get("orderId"));
context.wait("cooldown", Duration.ofSeconds(5));
return Map.of("orderId", order, "status", "processed");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker: The other languages run a process step here, so their records have three operations and this one has two. This matches them, compiles against 3.0.0, and records all three:

Suggested change
return Map.of("orderId", order, "status", "processed");
return context.step("process", Map.class, ctx -> Map.of("orderId", order, "status", "processed"));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ada37b2 / 49bc85f. Applied. It compiles against SDK 3.0.0.

This branch had an error being deployed

1 failed deployment
ai-pr-review-runtime — 49bc85f5 Deployed Oct 24, 2026 by ParidelPooya via ai-pr-review / Claude review / Generate Claude review #148
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants