Expand description
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:
- SDK interceptors — automatically attach OTel semantic-convention attributes to every AWS SDK call (DynamoDB, S3, SQS, …).
- 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.
- Environment resource detection — detect whether the process is running
on Lambda, ECS, EKS, or EC2 and populate the OTel
Resourceaccordingly.
The default feature set
(tracing-backend + env-lambda + extract-dynamodb + export-xray)
covers the most common Lambda workload with zero extra configuration.
§Quick Start — Lambda with DynamoDB
The fastest path to a fully instrumented Lambda function:
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
);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 activetracing::Span, which is then forwarded to OTel viatracing-opentelemetry. This is the recommended choice: it integrates naturally with thetracingecosystem and requires no extra setup.otel-backend— the interceptor manages OTel spans directly via theopentelemetryAPI.
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:
// 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`.
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 TowerLayerthat 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 completemain()function: telemetry init, SDK config/client singletons, Tower layer setup, andlambda_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
SdkTracerProviderwith the detected resource, X-Ray ID generator and a batch X-Ray exporter (whenexport-xrayis enabled). - Registers it as the global OTel tracer provider.
- Installs a
tracing-subscriberwith a JSON console layer and an OTel bridge layer (whentracing-backendis enabled).
§Manual Setup
When you need more control, wire the pieces together yourself:
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— forload_from_env()andSdkConfig. Pinned with thebehavior-version-latestfeature.aws_smithy_runtime_api— for thecontext::*types used in custominterceptor::AttributeExtractorimplementations.aws_smithy_types— forConfigBag, used inAttributeExtractormethod signatures.opentelemetry— forStatus,Value,KeyValue, etc.opentelemetry_sdk— forSdkTracerProvider,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— fortracing::Spanand the#[instrument]macro.tracing_subscriber— for users composing their own subscriber stack alongsideinit::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 forlambda_runtime::Errorandlambda_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 bymake_lambda_runtime!. Thetokioproc-macro resolves the crate by its absolute path (::tokio), so re-exporting it would not help. Lambda functions in Rust needtokioanyway.serde_json(orserde) — for typical Lambda event types. Lambda functions almost always need this for event/response (de)serialisation.
Re-exports§
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;
Modules§
- env
- OTel
Resourcedetection for AWS environments. - init
- Telemetry initialisation helpers and SDK client convenience macros.
- interceptor
- AWS SDK interceptor that automatically extracts OTel semantic-convention attributes from SDK calls.
- lambda
- Lambda support: Tower layer, per-invocation spans, and the
make_lambda_runtime!macro (env-lambdafeature). - span_
write - Backend-agnostic interface for writing attributes and status into a span.
Macros§
- aws_
sdk_ client_ provider - Declares a
OnceLock-backed AWS SDK client accessor function. - aws_
sdk_ config_ provider - Declares a
OnceLock-backedaws_sdk_config()accessor and an asyncsdk_config_init()initialiser. - make_
lambda_ runtime - Generates a complete
#[tokio::main] async fn main()for a Lambda function.