aws-durable-execution-sdk-python-telemetry-exporter 0.0.1__tar.gz → 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/.gitignore +1 -0
  2. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/PKG-INFO +401 -0
  3. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/README.md +378 -0
  4. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/pyproject.toml +8 -2
  5. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/__about__.py +1 -1
  6. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/__init__.py +3 -4
  7. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/src/aws_durable_execution_sdk_python_telemetry_exporter/_export_scheduler.py +656 -0
  8. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/dynamodb_exporter.py +40 -4
  9. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/lambda_log_exporter.py +90 -0
  10. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/otel_exporter.py +2 -1
  11. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/src/aws_durable_execution_sdk_python_telemetry_exporter/plugin.py +936 -0
  12. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/truncation.py +53 -11
  13. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/types.py +42 -1
  14. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/e2e/wait_suspend_resume_int_test.py +8 -5
  15. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/tests/test_error_and_result_format.py +415 -0
  16. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/tests/test_export_scheduler.py +1218 -0
  17. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_exporters.py +37 -9
  18. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/tests/test_package_metadata.py +122 -0
  19. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/tests/test_plugin.py +1948 -0
  20. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/tests/test_public_path_limits.py +468 -0
  21. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_shaping.py +64 -0
  22. aws_durable_execution_sdk_python_telemetry_exporter-0.0.2/tests/test_timestamp_format.py +301 -0
  23. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/PKG-INFO +0 -256
  24. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/README.md +0 -233
  25. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/src/aws_durable_execution_sdk_python_telemetry_exporter/_export_scheduler.py +0 -178
  26. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/lambda_log_exporter.py +0 -41
  27. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/src/aws_durable_execution_sdk_python_telemetry_exporter/plugin.py +0 -452
  28. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/tests/test_export_scheduler.py +0 -255
  29. aws_durable_execution_sdk_python_telemetry_exporter-0.0.1/tests/test_plugin.py +0 -663
  30. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/LICENSE +0 -0
  31. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/NOTICE +0 -0
  32. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/__init__.py +0 -0
  33. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/_common.py +0 -0
  34. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/aurora_exporter.py +0 -0
  35. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/cloudwatch_logs_exporter.py +0 -0
  36. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/eventbridge_exporter.py +0 -0
  37. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/file_exporter.py +0 -0
  38. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/firehose_exporter.py +0 -0
  39. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/http_exporter.py +0 -0
  40. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/opensearch_exporter.py +0 -0
  41. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/redshift_exporter.py +0 -0
  42. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/s3_exporter.py +0 -0
  43. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/exporters/sqs_exporter.py +0 -0
  44. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/operations_index.py +0 -0
  45. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/src/aws_durable_execution_sdk_python_telemetry_exporter/py.typed +0 -0
  46. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/__init__.py +0 -0
  47. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/e2e/__init__.py +0 -0
  48. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/e2e/exporters_lifecycle_int_test.py +0 -0
  49. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_aurora_exporter.py +0 -0
  50. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_cloudwatch_logs_exporter.py +0 -0
  51. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_config.py +0 -0
  52. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_dynamodb_exporter.py +0 -0
  53. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_eventbridge_exporter.py +0 -0
  54. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_file_exporter.py +0 -0
  55. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_firehose_exporter.py +0 -0
  56. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_http_exporter.py +0 -0
  57. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_opensearch_exporter.py +0 -0
  58. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_otel_exporter.py +0 -0
  59. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_redshift_exporter.py +0 -0
  60. {aws_durable_execution_sdk_python_telemetry_exporter-0.0.1 → aws_durable_execution_sdk_python_telemetry_exporter-0.0.2}/tests/test_sqs_exporter.py +0 -0
@@ -34,6 +34,7 @@ dist/
34
34
  **/build/
35
35
  **/*.zip
36
36
  .aws-sam/
37
+ .microvm-image-build/
37
38
  **/template.generated.json
38
39
 
39
40
  .env
@@ -0,0 +1,401 @@
1
+ Metadata-Version: 2.5
2
+ Name: aws-durable-execution-sdk-python-telemetry-exporter
3
+ Version: 0.0.2
4
+ Summary: Durable Execution Telemetry instrumentation plugin for the AWS Durable Execution SDK for Python
5
+ Project-URL: Documentation, https://github.com/aws/aws-durable-execution-sdk-python#readme
6
+ Project-URL: Issues, https://github.com/aws/aws-durable-execution-sdk-python/issues
7
+ Project-URL: Source, https://github.com/aws/aws-durable-execution-sdk-python
8
+ Author-email: AWS durable-execution-dev <durable-execution-dev@amazon.com>
9
+ License-Expression: Apache-2.0
10
+ Keywords: durable-execution,durable-execution-telemetry,observability
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: aws-durable-execution-sdk-python<4,>=3.0.0
20
+ Provides-Extra: s3
21
+ Requires-Dist: boto3>=1.26.0; extra == 's3'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # AWS Durable Execution SDK for Python — Durable Execution Telemetry plugin
25
+
26
+ Durable Execution Telemetry instrumentation plugin for the AWS Durable Execution SDK for
27
+ Python. A port of the JavaScript SDK's `durableExecutionTelemetry()` plugin: it listens to
28
+ the SDK's instrumentation hooks and emits one curated `DurableExecutionTelemetry` record
29
+ per execution to the configured exporters. The wire record keeps the JS
30
+ camelCase field names so records read identically across SDKs.
31
+
32
+ > **Experimental.** Like its JS counterpart, this plugin is experimental and may
33
+ > change or be removed in future releases.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install aws-durable-execution-sdk-python-telemetry-exporter
39
+ # with the S3 exporter's local-dev dependency:
40
+ pip install "aws-durable-execution-sdk-python-telemetry-exporter[s3]"
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```python
46
+ from aws_durable_execution_sdk_python import durable_execution
47
+ from aws_durable_execution_sdk_python_telemetry_exporter import (
48
+ DurableExecutionTelemetryConfig,
49
+ durable_execution_telemetry,
50
+ )
51
+ from aws_durable_execution_sdk_python_telemetry_exporter.exporters import S3Exporter
52
+
53
+ @durable_execution(
54
+ plugins=[
55
+ durable_execution_telemetry(
56
+ DurableExecutionTelemetryConfig(
57
+ exporters=[
58
+ S3Exporter(bucket="my-bucket", prefix="durable-execution-telemetry/")
59
+ ],
60
+ )
61
+ )
62
+ ]
63
+ )
64
+ def handler(event, context):
65
+ ...
66
+ ```
67
+
68
+ `durable_execution_telemetry()` returns a plugin *factory*, which is what the SDK's
69
+ `plugins` argument takes: the SDK calls its `create_plugin` once per invocation to
70
+ build that invocation's plugin instance. The factory holds the resolved
71
+ configuration and the exporters, so configuration is per handler while record
72
+ state is per invocation. Its type is `DurableExecutionTelemetryPluginFactory`, exported from
73
+ the package root for annotating a value you hold.
74
+
75
+ With no exporter configured, records are written to the function's own
76
+ CloudWatch log group as single JSON lines (the `LambdaLogExporter` default, see
77
+ below), carrying the name-keyed `operationsByName` summary. The `S3Exporter` writes the
78
+ lossless per-occurrence `operations` array, one object per execution
79
+ (upsert-by-execution-name, so re-emission overwrites rather than appends).
80
+
81
+ ## Exporters
82
+
83
+ All exporters live in `aws_durable_execution_sdk_python_telemetry_exporter.exporters` and
84
+ are re-exported from the package root. Each serializes the record as compact
85
+ JSON. Exporters that call AWS accept an injected `client=` for tests and use
86
+ the Lambda runtime's boto3 otherwise; none adds a required dependency.
87
+
88
+ | Exporter | Destination | Upsert | Operations shape | Default size limit |
89
+ | --- | --- | --- | --- | --- |
90
+ | `LambdaLogExporter` | Function's own log group | No | `operationsByName` | 256 KB |
91
+ | `CloudWatchLogsExporter` | Any log group, one stream per day | No | `operationsByName` | 256 KB |
92
+ | `S3Exporter` | S3 object per execution | Yes (key) | `operations` | 5 MB |
93
+ | `DynamoDBExporter` | DynamoDB item | Configurable | `operationsByName` | 400 KB |
94
+ | `AuroraExporter` | Aurora MySQL/PostgreSQL row (Data API) | Yes (upsert) | full record as JSON column | 1 MB |
95
+ | `RedshiftExporter` | Redshift row (Data API) | Yes (MERGE) | full record as SUPER column | 1 MB |
96
+ | `OpenSearchExporter` | OpenSearch document | Yes (`_id`) | `operations` | 10 MB |
97
+ | `FirehoseExporter` | Firehose delivery stream | N/A | `operations_format` | 1 MB |
98
+ | `EventBridgeExporter` | EventBridge event | N/A | `operations_format` | 256 KB |
99
+ | `SQSExporter` | SQS message | N/A | `operations_format` | 256 KB |
100
+ | `OTelExporter` | OTLP/HTTP logs endpoint | N/A | `operations_format` | 1 MB |
101
+ | `HttpExporter` | Any HTTP endpoint | N/A | `operations_format` | none |
102
+ | `FileExporter` | Directory (EFS, mount, `/tmp`) | Configurable | `operations_format` | none |
103
+
104
+ `operations_format` is `"array"` (default), `"by-name"`, or `"both"`
105
+ (`OperationsFormat`). `max_record_size_bytes` raises or lowers an exporter's
106
+ size limit; omitting it keeps the default. `HttpExporter` and `FileExporter`
107
+ have no default and do not truncate unless a limit is set.
108
+
109
+ ### LambdaLogExporter
110
+
111
+ Prints one line per record to stdout, which Lambda sends to the function's own
112
+ log group. The line is the Lambda JSON log envelope, with keys in this order:
113
+
114
+ ```json
115
+ {"timestamp":"2026-01-01T00:00:01.234Z","level":"INFO","message":"<compact record JSON>"}
116
+ ```
117
+
118
+ `timestamp` is UTC with millisecond precision and a `Z` suffix. `message` is
119
+ the compact record JSON as a string; parse it to get the record. The exporter
120
+ writes the envelope itself because Lambda captures Python `print` output as
121
+ plain text, in both the plain text and the JSON log format, and Lambda does not
122
+ double-encode output that is already JSON (see
123
+ [Log and monitor Python Lambda functions](https://docs.aws.amazon.com/lambda/latest/dg/python-logging.html)
124
+ and [Lambda log formats](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs-logformat.html)).
125
+
126
+ `max_record_size_bytes` (default 256,000 bytes) limits the printed line: the
127
+ envelope, including the JSON escaping of `message`, in UTF-8 bytes without the
128
+ trailing newline. Truncation measures that line in one pass. A CloudWatch Logs
129
+ event can be up to 1 MB
130
+ ([PutLogEvents](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html)).
131
+ U+2028 and U+2029 are written as `\u2028` and `\u2029`, so a tool that treats
132
+ them as line breaks still sees one line per record. A lone surrogate (a string
133
+ character with no UTF-8 encoding) is written as its `\uXXXX` escape, so
134
+ printing it never fails. The limit counts these escapes; other exporters'
135
+ limits count the characters as they write them.
136
+
137
+ The lines have `level` `INFO`. With the JSON log format and an application log
138
+ level of `WARN` or above, Lambda drops them. With the Text log format, Lambda
139
+ does not filter them.
140
+
141
+ ### CloudWatchLogsExporter
142
+
143
+ Writes one `PutLogEvents` event per record to `log_group_name`, in a stream
144
+ named `{log_stream_prefix}{YYYY}/{MM}/{DD}` (default prefix `durable-execution-telemetry/`).
145
+ IAM: `logs:CreateLogStream`, `logs:PutLogEvents` on the log group.
146
+
147
+ ```python
148
+ CloudWatchLogsExporter(log_group_name="/custom/durable-execution-telemetry")
149
+ ```
150
+
151
+ ### DynamoDBExporter
152
+
153
+ `PutItem` keyed by `partition_key` (default `pk`) = `executionArn`. With the
154
+ default `sort_key="sk"` (= `emittedAt`) every export adds an item; pass
155
+ `sort_key=None` for a key-only table that upserts. IAM: `dynamodb:PutItem`.
156
+ A number DynamoDB cannot store (more than 38 significant digits, a magnitude
157
+ outside 1E-130 to 9.99...E+125, NaN or Infinity) is written as a string
158
+ attribute holding its JSON text, so it never drops the item. The same field can
159
+ therefore be a number (`N`) in one item and a string (`S`) in another; readers
160
+ that filter or compare on the attribute type should allow for both. An integer
161
+ with more than 38 digits but at most 38 significant digits, such as `10**38`,
162
+ is stored as a number.
163
+
164
+ ```python
165
+ DynamoDBExporter(table_name="durable-execution-telemetry")
166
+ ```
167
+
168
+ ### AuroraExporter
169
+
170
+ Upserts a row by `execution_arn` through the RDS Data API; `engine` is
171
+ `"postgresql"` or `"mysql"` and selects the dialect. Columns: `execution_arn,
172
+ execution_name, function_name, status, start_time, end_time, duration_ms,
173
+ record_json, emitted_at`. IAM: `rds-data:ExecuteStatement`,
174
+ `secretsmanager:GetSecretValue`.
175
+
176
+ ```python
177
+ AuroraExporter(
178
+ resource_arn="arn:aws:rds:us-east-1:123456789012:cluster:my-cluster",
179
+ secret_arn="arn:aws:secretsmanager:us-east-1:123456789012:secret:my-db-creds",
180
+ database="telemetry",
181
+ engine="postgresql",
182
+ )
183
+ ```
184
+
185
+ ### RedshiftExporter
186
+
187
+ `MERGE` by `execution_arn` through the Redshift Data API into
188
+ `{schema}.{table}` (default `public.durable_execution_telemetry`, same columns as Aurora,
189
+ `record_json` as `SUPER`). Provide `workgroup_name` (Serverless) or
190
+ `cluster_identifier` (provisioned, with `db_user` or `secret_arn`). IAM:
191
+ `redshift-data:ExecuteStatement` plus `redshift-serverless:GetCredentials` or
192
+ `secretsmanager:GetSecretValue`. The statement is submitted and not awaited, so
193
+ statement failures are not reported and `on-change` records may land out of
194
+ order; prefer the default `on-complete` emit mode with this exporter.
195
+
196
+ ```python
197
+ RedshiftExporter(database="telemetry", workgroup_name="telemetry-wg")
198
+ ```
199
+
200
+ ### OpenSearchExporter
201
+
202
+ `PUT {endpoint}/{index_name}/_doc/{executionArn}` (default index
203
+ `durable-execution-telemetry`). `auth="sigv4"` (default) signs with the runtime's
204
+ credentials via botocore; `auth="basic"` uses `username`/`password`. IAM:
205
+ `es:ESHttpPut` on the domain.
206
+
207
+ ```python
208
+ OpenSearchExporter(endpoint="https://my-domain.us-east-1.es.amazonaws.com", region="us-east-1")
209
+ ```
210
+
211
+ ### FirehoseExporter
212
+
213
+ `PutRecord` of one JSON line (trailing newline) per record. IAM:
214
+ `firehose:PutRecord`.
215
+
216
+ ```python
217
+ FirehoseExporter(delivery_stream_name="durable-execution-telemetry-stream")
218
+ ```
219
+
220
+ ### EventBridgeExporter
221
+
222
+ `PutEvents` with `Source` (default `software.amazon.lambda.durable.telemetry`),
223
+ `DetailType` = record status, `Detail` = record. All arguments optional. IAM:
224
+ `events:PutEvents`.
225
+
226
+ ```python
227
+ EventBridgeExporter(event_bus_name="default")
228
+ ```
229
+
230
+ ### SQSExporter
231
+
232
+ `SendMessage` with the record as body and `status`/`functionName` message
233
+ attributes. A `.fifo` queue URL enables `MessageGroupId` (default
234
+ `executionArn`, or `message_group_id`) and a deduplication id of
235
+ `executionArn:emittedAt`. IAM: `sqs:SendMessage`.
236
+
237
+ ```python
238
+ SQSExporter(queue_url="https://sqs.us-east-1.amazonaws.com/123456789012/telemetry")
239
+ ```
240
+
241
+ ### OTelExporter
242
+
243
+ POSTs one OTLP `ExportLogsServiceRequest` (`http/json` only) per record to
244
+ `endpoint`; identity fields become attributes and the record is the log body.
245
+ Pass vendor auth in `headers`. No IAM.
246
+
247
+ ```python
248
+ OTelExporter(endpoint="https://otlp.vendor.com/v1/logs", headers={"x-api-key": "..."})
249
+ ```
250
+
251
+ ### HttpExporter
252
+
253
+ `POST` (or `method="PUT"`) the record as JSON to `url` with
254
+ `Content-Type: application/json` plus `headers`; a non-2xx status raises.
255
+ `timeout_ms` defaults to 10000 and currently applies to each socket operation
256
+ (the connect, each write of the request, and each read of the status line,
257
+ headers, and a non-2xx error body; a 2xx body is never read) rather than to the
258
+ whole request, so an endpoint that consumes or responds slowly can hold the
259
+ export open longer than `timeout_ms`; a future release may enforce it as a
260
+ whole-request deadline. No IAM.
261
+
262
+ ```python
263
+ HttpExporter(url="https://hooks.example.com/telemetry", headers={"Authorization": "Bearer ..."})
264
+ ```
265
+
266
+ ### FileExporter
267
+
268
+ `mode="ndjson"` (default) appends to `{directory}/{YYYY-MM-DD}.ndjson`;
269
+ `mode="json"` writes `{directory}/{executionName}.json`, overwriting on update.
270
+ For Lambda use an EFS mount (IAM: `elasticfilesystem:ClientMount`,
271
+ `elasticfilesystem:ClientWrite`) or `/tmp` for testing. Appends from many
272
+ concurrent environments to one NDJSON file on a network file system can
273
+ interleave; use `mode="json"` when several environments share a directory.
274
+
275
+ ```python
276
+ FileExporter(directory="/mnt/efs/durable-execution-telemetry")
277
+ ```
278
+
279
+ Emission behavior, record schema (`recordType: DurableExecutionTelemetry`,
280
+ `schemaVersion: "1.0"`), sampling, content configuration (input/output
281
+ omission, `include_errors`, per-operation result opt-in), truncation phases,
282
+ and `top-level` vs `full-tree` operation detail all mirror the JS plugin.
283
+ Behavior is validated cross-SDK by the `telemetry` conformance suite
284
+ (`aws-durable-execution-conformance-tests-telemetry`).
285
+
286
+ Record timestamps (`emittedAt`, `startTime`, `endTime`, and each operation's
287
+ `startTime` and `endTime`) use one fixed-width UTC format:
288
+ `YYYY-MM-DDTHH:MM:SS.sssZ`, for example `2026-10-23T18:42:40.784Z`. The
289
+ fraction always has exactly 3 digits, and the suffix is always `Z`. The plugin
290
+ converts each time to UTC and truncates it to milliseconds. JavaScript's
291
+ `Date.prototype.toISOString()` writes the same format. Stores such as DynamoDB
292
+ compare these values as text. Text order equals time order only when every
293
+ value has the same width. So a text comparison of two record timestamps orders
294
+ them by time.
295
+
296
+ `emittedAt` strictly increases within one invocation. In `on-change` mode, one
297
+ invocation can build two records in the same millisecond. The SQS FIFO
298
+ deduplication id and the default DynamoDB sort key both use `emittedAt`. So
299
+ equal values would drop or overwrite a record. So when the clock has not
300
+ passed the previous `emittedAt`, the plugin writes the previous value plus 1
301
+ millisecond.
302
+
303
+ > **Note (asynchronous export).** Export rendering, truncation, `export()`, and
304
+ > `flush()` run on one lazy background worker per registered factory. Checkpoint
305
+ > hooks only replace the latest pending snapshot and wake the worker. Consecutive
306
+ > `on-change` snapshots may coalesce while an export is in flight. Every
307
+ > sampled-in invocation end waits for a covering flush to complete after its
308
+ > drain starts, including an end that emitted no record. Overlapping ends may
309
+ > share a flush when it covers both entry snapshots. Only a sampled-out execution
310
+ > neither exports nor flushes. Exporter failures are logged and isolated; this is
311
+ > best-effort telemetry, not a guarantee of delivery to the destination.
312
+
313
+ > **Note (invocation-end latency under concurrency).** The drain an invocation
314
+ > end performs captures a finite snapshot of work already scheduled across the
315
+ > factory's executions. It waits until those records have been offered to the
316
+ > exporters and a covering flush completes. If a pending record is coalesced, its
317
+ > replacement satisfies the earlier obligation. Once the worker takes that
318
+ > replacement, later records for the same execution belong to a new queue slot.
319
+ > Later drains cannot extend an earlier flush target: they share a covering
320
+ > target or queue a subsequent one. Thus continuing arrivals do not move an
321
+ > earlier drain's finish line. One worker serializes exports and flushes, so
322
+ > invocation-end latency still includes other executions' work present at entry
323
+ > and any earlier flushes. Budget for that finite backlog, especially when an
324
+ > exporter makes a network call per record.
325
+
326
+ ## Error names and causes
327
+
328
+ Every error in a record (the execution `error`, each `operations[].error`, and
329
+ each `operationsByName.<name>.error`) has the shape
330
+ `{"name": ..., "message": ..., "cause": ...}`, with keys in that order. `name`
331
+ is the SDK's error type, unchanged, and `message` is the error message. `cause`
332
+ has the same shape, recursively, and is present only when the SDK provides a
333
+ cause.
334
+
335
+ In Python, the plugin hooks carry only an `ErrorObject` (type, message, data,
336
+ stack trace) with no cause, so no error in a Python record has a `cause`: not
337
+ the execution `error`, not an operation error. The exporter does not build a
338
+ cause from other operations or from `ErrorData`. `include_errors=False`
339
+ removes operation errors.
340
+
341
+ Every `name` and `message` is capped at 8,192 characters, counted as Unicode
342
+ code points (a Python `str` is a sequence of code points, so a cut never splits
343
+ a character). A longer value keeps its first 8,192 code points followed by `…`.
344
+
345
+ When a record is over its exporter's size limit, truncation first removes every
346
+ `cause` and sets `droppedCauses: true`, then runs its usual steps (operation
347
+ results, then whole operations, then input, then output). Python records have
348
+ no cause, so the first step never changes them. With the name and message
349
+ caps, a record whose operations, input and output are dropped fits any limit of
350
+ 64 KB or more, assuming ARN fields of normal length (a few hundred bytes, as in
351
+ real Lambda ARNs) and an error `name` of normal length (a type name such as
352
+ `module.QualName`). Without the `name` assumption, the worst case (a name and
353
+ message of 8,192 control characters, each escaped as `\u00XX`) fits any limit
354
+ of 128 KB (131,072 bytes) or more.
355
+
356
+ Names are each language's own and differ across SDKs. For example, a step that
357
+ throws `PaymentError` can be reported as `StepError` in JS,
358
+ `aws_durable_execution_sdk_python.exceptions.StepError` or `PaymentError` in
359
+ Python, and `StepFailedException` or `PaymentError` in Java. Query error names
360
+ per SDK, or query on `message` and the `cause` chain.
361
+
362
+ ## Operation results
363
+
364
+ An opted-in operation `result` (`OperationOverride(result=...)`) is passed to
365
+ the transform as a plain JSON value. When the checkpointed string is the
366
+ default serializer's (`ExtendedTypeSerDes`) envelope, a JSON object whose keys
367
+ are exactly `t` and `v` with a string `t`, it is decoded with that serializer
368
+ and converted to plain JSON:
369
+
370
+ - `int`: unchanged when its absolute value is at most `2**53 - 1`
371
+ (9007199254740991), the largest integer a JavaScript reader parses exactly.
372
+ Otherwise `str(value)`, for example `"12345678901234567"`.
373
+ - `Decimal`: an integer when it is integral and its absolute value is at most
374
+ `2**53 - 1`. Otherwise a float when the float round-trips exactly and is
375
+ within DynamoDB's number range (magnitude 1E-130 to 9.99...E+125). Otherwise `str(value)`, for example `"9007199254740993"`,
376
+ `"12345678901234567.89"` or `"1E+299"`. NaN and Infinity become `null`.
377
+ Every zero, of any sign or exponent (`Decimal("-0")`, `Decimal("-0E+20")`),
378
+ becomes `0` and loses its sign.
379
+ - `datetime` and `date`: `isoformat()`. `UUID`: string. `bytes`: base64.
380
+ `tuple`: list. `BatchResult`: `to_dict()`.
381
+
382
+ Any other result is parsed as JSON, or kept as the raw string when it is not
383
+ JSON. In parsed JSON, NaN and Infinity become `null`, and an integer whose
384
+ absolute value is greater than `2**53 - 1` becomes its decimal string, as
385
+ above; nothing else changes. A
386
+ result that fails to decode keeps its parsed or raw value, and the rest of the
387
+ record is still emitted.
388
+
389
+ The hooks do not say which serializer wrote a result, so the envelope is
390
+ recognized by shape alone. A result written by `JsonSerDes` or a custom
391
+ `SerDes` that has this exact shape is decoded too: `{"t": "s", "v": "x"}` is
392
+ reported as `"x"`. Only the core serializer's known tags are decoded, and the
393
+ core may coerce `v` while decoding: `{"t": "b", "v": "yes"}` is reported as
394
+ `true`. An unknown tag, such as `{"t": "temperature", "v": 21.5}`, fails to
395
+ decode and passes through unchanged.
396
+
397
+ ## Requirements
398
+
399
+ - `aws-durable-execution-sdk-python` with the plugin invocation hooks that
400
+ surface `execution_input` / `execution_result` (included since the version
401
+ this package declares as its minimum).