1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
//! Out-of-the-box OpenTelemetry/X-Ray instrumentation for the AWS SDK for Rust,
//! with first-class support for AWS Lambda.
//!
//! This crate wires together three concerns in one place:
//!
//! 1. **SDK interceptors** — automatically attach OTel semantic-convention
//! attributes to every AWS SDK call (DynamoDB, S3, SQS, …).
//! 2. **Lambda Tower layer** — create a per-invocation span covering the handler,
//! propagate the X-Ray trace context, track cold-starts, and flush the
//! exporter after each invocation.
//! 3. **Environment resource detection** — detect whether the process is running
//! on Lambda, ECS, EKS, or EC2 and populate the OTel [`Resource`] accordingly.
//!
//! The default feature set
//! (`tracing-backend` + `env-lambda` + `extract-dynamodb` + `export-xray`)
//! covers the most common Lambda workload with zero extra configuration.
//!
//! [`Resource`]: opentelemetry_sdk::Resource
//!
//! # Quick Start — Lambda with DynamoDB
//!
//! The fastest path to a fully instrumented Lambda function:
//!
//! ```no_run
//! # mod private {
//! use awssdk_instrumentation::lambda::{LambdaError, LambdaEvent};
//! use serde_json::Value;
//!
//! // 1. Declare the handler.
//! async fn handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError> {
//! // Use dynamodb_client() anywhere in your crate — the interceptor
//! // automatically records DynamoDB spans.
//! let resp = dynamodb_client().get_item().table_name("orders").send().await?;
//! Ok(event.payload)
//! }
//!
//! // 2. One macro call generates main(), telemetry init, and the Tower layer.
//! // Here you also create a DynamoDB client singleton with the interceptor pre-attached.
//! awssdk_instrumentation::make_lambda_runtime!(
//! handler,
//! dynamodb_client() -> aws_sdk_dynamodb::Client
//! );
//! # }
//! # fn dynamodb_client() {}
//! # fn aws_sdk_config() {}
//! # fn main() {}
//! ```
//!
//! With this setup, you only need the following crates as direct dependencies
//! in your `Cargo.toml`: `awssdk-instrumentation`, `tokio` (for the
//! `#[tokio::main]` attribute generated by the macro), `serde_json` (for the
//! `Value` event type), and the `aws-sdk-*` service crates you actually call.
//!
//! # Key Concepts
//!
//! ## Backend
//!
//! Two mutually-exclusive (but co-installable) backends bridge the AWS SDK
//! interceptor and Lambda Tower layer to OpenTelemetry:
//!
//! - **`tracing-backend`** (default) — the interceptor writes attributes into
//! the active [`tracing::Span`], which is then forwarded to OTel via
//! `tracing-opentelemetry`. This is the recommended choice: it integrates
//! naturally with the `tracing` ecosystem and requires no extra setup.
//! - **`otel-backend`** — the interceptor manages OTel spans directly via the
//! `opentelemetry` API.
//!
//! At least one backend must be enabled; the crate will fail to compile
//! otherwise.
//!
//! The [`interceptor::DefaultInterceptor`] type alias always resolves to the
//! right interceptor for the active backend.
//!
//! ## Interceptors
//!
//! [`interceptor::DefaultInterceptor`] implements the AWS SDK `Intercept` trait.
//! Attach it to any SDK client config to get automatic span attribute extraction:
//!
//! ```no_run
//! # use awssdk_instrumentation::interceptor::DefaultInterceptor;
//! // Attach the interceptor when building the SDK client config.
//! // `aws_config` is re-exported by this crate, so you can either depend on
//! // it directly or reach it via `awssdk_instrumentation::aws_config`.
//! // The `aws-sdk-*` service crates (e.g. `aws-sdk-dynamodb`) are *not*
//! // re-exported and must be added to your own `Cargo.toml`.
//! # async fn example() {
//! let sdk_config = aws_config::load_from_env().await;
//! let dynamo = aws_sdk_dynamodb::Client::from_conf(
//! aws_sdk_dynamodb::config::Builder::from(&sdk_config)
//! .interceptor(DefaultInterceptor::new())
//! .build(),
//! );
//! # }
//! ```
//!
//! The [`interceptor::DefaultExtractor`] inside the interceptor dispatches to
//! per-service extractors (DynamoDB, S3, SQS) and then runs any user-registered
//! hooks or [`interceptor::AttributeExtractor`] implementations.
//!
//! ## Lambda support
//!
//! The [`lambda`] module (feature `env-lambda`) provides:
//!
//! - [`lambda::layer::TracingLayer`] — a Tower `Layer` that wraps the Lambda
//! runtime service, creates a span per invocation, and flushes the exporter
//! when the invocation future drops.
//! - [`make_lambda_runtime!`] — a macro that generates a complete `main()`
//! function: telemetry init, SDK config/client singletons, Tower layer setup,
//! and `lambda_runtime::Runtime::run()`.
//!
//! ## Resource detection
//!
//! [`env::default_resource()`] probes the environment at startup and returns an
//! OTel [`Resource`] populated with the appropriate semantic-convention
//! attributes. It tries Lambda first (if feature `env-lambda`),
//! then ECS (if feature `env-ecs`),
//! then EKS (if feature `env-eks`),
//! then EC2 (if feature `env-ec2`),
//! and then falls back to a minimal `cloud.provider = aws` resource.
//!
//! ## Telemetry initialisation
//!
//! [`init::default_telemetry_init()`] is a one-call setup that:
//!
//! - Builds a [`SdkTracerProvider`] with the detected resource, X-Ray ID
//! generator and a batch X-Ray exporter (when `export-xray` is enabled).
//! - Registers it as the global OTel tracer provider.
//! - Installs a `tracing-subscriber` with a JSON console layer and an OTel
//! bridge layer (when `tracing-backend` is enabled).
//!
//! [`SdkTracerProvider`]: opentelemetry_sdk::trace::SdkTracerProvider
//!
//! # Manual Setup
//!
//! When you need more control, wire the pieces together yourself:
//!
//! ```no_run
//! use serde_json::Value;
//! use awssdk_instrumentation::{
//! init::default_telemetry_init,
//! interceptor::DefaultInterceptor,
//! lambda::{LambdaError, LambdaEvent, OTelFaasTrigger, layer::DefaultTracingLayer},
//! };
//!
//! // 1. Declare the handler.
//! async fn handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError> {
//! todo!("Do Stuff...");
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), LambdaError> {
//! // Initialise telemetry (sets global tracer provider + tracing subscriber).
//! let tracer_provider = default_telemetry_init();
//!
//! // Build an SDK client with the interceptor attached.
//! let sdk_config = aws_config::load_from_env().await;
//! let dynamo = aws_sdk_dynamodb::Client::from_conf(
//! aws_sdk_dynamodb::config::Builder::from(&sdk_config)
//! .interceptor(DefaultInterceptor::new())
//! .build(),
//! );
//!
//! // Wrap the Lambda runtime with the Tower layer.
//! lambda_runtime::Runtime::new(lambda_runtime::service_fn(handler))
//! .layer(
//! DefaultTracingLayer::new(move || {
//! let _ = tracer_provider.force_flush();
//! })
//! .with_trigger(OTelFaasTrigger::Http),
//! )
//! .run()
//! .await
//! }
//! ```
//!
//! # Feature Flags
//!
//! Features are grouped by category. Items marked ✅ are enabled by default.
//!
//! ## Backend
//!
//! | Feature | Default | Description |
//! |-------------------|---------|-------------|
//! | `tracing-backend` | ✅ | Writes span attributes via `tracing::Span` + `tracing-opentelemetry` |
//! | `otel-backend` | | Manages OTel spans directly without `tracing` |
//!
//! ## Environment detection
//!
//! | Feature | Default | Description |
//! |-------------|---------|-------------|
//! | `env-lambda`| ✅ | Lambda Tower layer, resource detector, `make_lambda_runtime!` |
//! | `env-ecs` | | ECS resource detector (reads container metadata endpoint) |
//! | `env-eks` | | EKS resource detector (reads k8s service account + IMDS) |
//! | `env-ec2` | | EC2 resource detector (reads IMDSv2) |
//!
//! ## Service attribute extraction
//!
//! | Feature | Default | Description |
//! |--------------------|---------|-------------|
//! | `extract-dynamodb` | ✅ | DynamoDB OTel semantic-convention attributes |
//! | `extract-s3` | | S3 OTel semantic-convention attributes |
//! | `extract-sqs` | | SQS OTel semantic-convention attributes |
//!
//! ## Export
//!
//! | Feature | Default | Description |
//! |---------------|---------|-------------|
//! | `export-xray` | ✅ | X-Ray ID generator, propagator, and daemon exporter via `opentelemetry-aws` |
//!
//! When `export-xray` is enabled, the `opentelemetry_aws` crate is re-exported
//! at the crate root so you can access the X-Ray propagator and exporter types
//! directly.
//!
//! # Re-exported crates
//!
//! To minimise the dependencies users need to declare in their own
//! `Cargo.toml`, this crate re-exports every external crate that appears in
//! its public API. You can reach any of them via `awssdk_instrumentation::<crate>`.
//!
//! Always re-exported at the crate root:
//!
//! - [`aws_config`] — for `load_from_env()` and `SdkConfig`. Pinned with the
//! `behavior-version-latest` feature.
//! - [`aws_smithy_runtime_api`] — for the `context::*` types used in custom
//! [`interceptor::AttributeExtractor`] implementations.
//! - [`aws_smithy_types`] — for `ConfigBag`, used in `AttributeExtractor` method signatures.
//! - [`opentelemetry`] — for `Status`, `Value`, `KeyValue`, etc.
//! - [`opentelemetry_sdk`] — for `SdkTracerProvider`, `Resource`, etc.
//! - [`opentelemetry_semantic_conventions`] — for the attribute key constants
//! used when writing custom extractors.
//!
//! Re-exported when feature `tracing-backend` is enabled (the default):
//!
//! - [`tracing`] — for `tracing::Span` and the `#[instrument]` macro.
//! - [`tracing_subscriber`] — for users composing their own subscriber stack
//! alongside [`init::default_tracing_otel_layer`] /
//! [`init::default_tracing_console_layer`].
//! - [`tracing_opentelemetry`] — for users replacing the default OTel bridge
//! layer entirely.
//!
//! Re-exported when feature `env-lambda` is enabled (the default):
//!
//! - [`lambda::lambda_runtime`] — the Lambda runtime crate.
//! - [`lambda::LambdaError`] / [`lambda::LambdaEvent`] — convenience aliases
//! for `lambda_runtime::Error` and `lambda_runtime::LambdaEvent`.
//!
//! Re-exported when feature `export-xray` is enabled (the default):
//!
//! - [`opentelemetry_aws`] — for the X-Ray ID generator, propagator, and
//! daemon exporter types.
//!
//! ## Crates you still need to add as direct dependencies
//!
//! - The `aws-sdk-*` service crates you actually use (`aws-sdk-dynamodb`,
//! `aws-sdk-s3`, …). These are not re-exported because they are the user's
//! primary dependencies.
//! - `tokio` — required for the `#[tokio::main]` attribute generated by
//! [`make_lambda_runtime!`]. The `tokio` proc-macro resolves the crate by
//! its absolute path (`::tokio`), so re-exporting it would not help. Lambda
//! functions in Rust need `tokio` anyway.
//! - `serde_json` (or `serde`) — for typical Lambda event types. Lambda
//! functions almost always need this for event/response (de)serialisation.
// Crate root — re-exports and feature-gated module declarations.
compile_error!;
pub use opentelemetry_aws;
pub use tracing;
pub use tracing_opentelemetry;
pub use tracing_subscriber;
pub use aws_config;
pub use aws_smithy_runtime_api;
pub use aws_smithy_types;
pub use opentelemetry;
pub use opentelemetry_sdk;
pub use opentelemetry_semantic_conventions;