awssdk_instrumentation/lambda/macros.rs
1//! The [`make_lambda_runtime!`] macro and its `default_flush_tracer` helper.
2//!
3//! [`make_lambda_runtime!`] generates a complete `#[tokio::main] async fn main()`
4//! that wires together telemetry initialisation, optional SDK client singletons,
5//! and the Lambda runtime with the [`super::layer::DefaultTracingLayer`] applied.
6//!
7//! ## Macro syntax
8//!
9//! ```text
10//! make_lambda_runtime!(
11//! handler_fn
12//! [, trigger = OTelFaasTrigger::Http]
13//! [, telemetry_init = my_telemetry_init]
14//! [, client_fn() -> SdkClientType]*
15//! );
16//! ```
17//!
18//! All parameters after `handler_fn` are optional:
19//!
20//! - `trigger` — sets the `faas.trigger` attribute (default: `Http`)
21//! - `telemetry_init` — custom telemetry init function with signature
22//! `fn() -> SdkTracerProvider` (default: [`crate::init::default_telemetry_init`])
23//! - `client_fn() -> SdkClientType` — zero or more SDK client declarations;
24//! each generates a `OnceLock`-backed accessor with
25//! [`crate::interceptor::DefaultInterceptor`] pre-attached
26//!
27//! ## Prerequisites
28//!
29//! The macro emits `#[tokio::main]` on the generated `main` function. Because
30//! the `tokio` proc-macro references itself by absolute path (`tokio`),
31//! you must add `tokio` to your own `Cargo.toml`.
32//!
33//! ## Example
34//!
35//! ```no_run
36//! use awssdk_instrumentation::lambda::{LambdaError, LambdaEvent};
37//! use serde_json::Value;
38//!
39//! async fn handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError> {
40//! Ok(event.payload)
41//! }
42//!
43//! // Minimal: just the handler, defaults to Http trigger.
44//! awssdk_instrumentation::make_lambda_runtime!(handler);
45//! ```
46//!
47//! [`make_lambda_runtime!`]: crate::make_lambda_runtime
48
49// make_lambda_runtime! macro — generates main(), tracer init, instrumented
50// SDK clients, Tower layer setup, and Lambda runtime execution.
51
52use opentelemetry_sdk::trace::SdkTracerProvider;
53
54/// Flushes the given [`SdkTracerProvider`], logging the outcome.
55///
56/// Called by the [`make_lambda_runtime!`]-generated flush closure after each invocation.
57#[doc(hidden)]
58pub fn default_flush_tracer(tracer_provider: &SdkTracerProvider) {
59 match tracer_provider.force_flush() {
60 Ok(_) => {
61 log::info!("TracingProviderFlusher: Flushed tracing provider");
62 }
63 Err(e) => {
64 log::warn!("Could not flush tracing provider: {e}");
65 }
66 }
67}
68
69/// Generates a complete `#[tokio::main] async fn main()` for a Lambda function.
70///
71/// This macro wires together telemetry initialisation, optional AWS SDK client
72/// singletons, and the Lambda runtime with the [`DefaultTracingLayer`] applied.
73/// It is the recommended entry point for Lambda functions using this crate.
74///
75/// # Syntax
76///
77/// ```text
78/// make_lambda_runtime!(
79/// handler_fn
80/// [, trigger = OTelFaasTrigger::Variant]
81/// [, telemetry_init = my_telemetry_init_fn]
82/// [, client_fn() -> SdkClientType]*
83/// );
84/// ```
85///
86/// All parameters after `handler_fn` are optional and can appear in any order:
87///
88/// - **`handler_fn`** *(required)* — path to the async handler function.
89/// - **`trigger`** — the [`OTelFaasTrigger`] variant for the `faas.trigger`
90/// attribute. Defaults to [`OTelFaasTrigger::Http`].
91/// - **`telemetry_init`** — a custom telemetry init function with signature
92/// `fn() -> SdkTracerProvider`. Defaults to [`default_telemetry_init`].
93/// - **`client_fn() -> SdkClientType`** — zero or more SDK client declarations.
94/// Each generates a `OnceLock`-backed accessor with [`DefaultInterceptor`]
95/// pre-attached.
96///
97/// # Prerequisites
98///
99/// `tokio` must be a direct dependency of your crate. Lambda functions in Rust
100/// need `tokio` anyway.
101///
102/// # Examples
103///
104/// Minimal usage — just the handler:
105///
106/// ```no_run
107/// use awssdk_instrumentation::lambda::{LambdaError, LambdaEvent};
108/// use serde_json::Value;
109///
110/// async fn handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError> {
111/// Ok(event.payload)
112/// }
113///
114/// awssdk_instrumentation::make_lambda_runtime!(handler);
115/// ```
116///
117/// With a DynamoDB client and a datasource trigger:
118///
119/// ```no_run
120/// # mod private {
121/// use awssdk_instrumentation::lambda::{LambdaError, LambdaEvent, OTelFaasTrigger};
122/// use serde_json::Value;
123///
124/// async fn handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError> {
125/// let _client = dynamodb_client();
126/// Ok(event.payload)
127/// }
128///
129/// awssdk_instrumentation::make_lambda_runtime!(
130/// handler,
131/// trigger = OTelFaasTrigger::Datasource,
132/// dynamodb_client() -> aws_sdk_dynamodb::Client
133/// );
134/// # }
135/// # fn dynamodb_client() {}
136/// # fn aws_sdk_config() {}
137/// # fn main() {}
138/// ```
139///
140/// [`DefaultTracingLayer`]: crate::lambda::layer::DefaultTracingLayer
141/// [`OTelFaasTrigger`]: crate::lambda::OTelFaasTrigger
142/// [`OTelFaasTrigger::Http`]: crate::lambda::OTelFaasTrigger::Http
143/// [`default_telemetry_init`]: crate::init::default_telemetry_init
144/// [`DefaultInterceptor`]: crate::interceptor::DefaultInterceptor
145/// [`aws_sdk_config_provider!`]: crate::aws_sdk_config_provider
146#[macro_export]
147macro_rules! make_lambda_runtime {
148 (
149 internal
150 $handler:path,
151 telemetry_init = $telemetry_init:path,
152 trigger = $trigger:expr
153 ) => {
154 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $telemetry_init, trigger = $trigger ;);
155 };
156 (
157 internal
158 $handler:path,
159 telemetry_init = $telemetry_init:path,
160 trigger = $trigger:expr
161 $(, $name:ident() -> $client:ty)+
162 ) => {
163 $crate::aws_sdk_config_provider!();
164 $(
165 $crate::aws_sdk_client_provider!($name() -> $client);
166 )+
167 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $telemetry_init, trigger = $trigger ; with_code sdk_config_init().await;);
168 };
169 (
170 internal
171 $handler:path,
172 telemetry_init = $telemetry_init:path,
173 trigger = $trigger:expr ;
174 $(with_code $($code:tt)+)?
175 ) => {
176
177 #[tokio::main]
178 async fn main() -> Result<(), $crate::lambda::lambda_runtime::Error> {
179
180 const _: fn() = || {
181 fn _test_telemetry_init(_f: fn() -> $crate::opentelemetry_sdk::trace::SdkTracerProvider) {}
182 _test_telemetry_init($telemetry_init)
183 };
184 let tracer_provider = $telemetry_init();
185
186 $($($code)+)?
187
188
189 $crate::lambda::lambda_runtime::Runtime::new($crate::lambda::lambda_runtime::service_fn($handler))
190 .layer(
191 <$crate::lambda::layer::DefaultTracingLayer<_>>::new(move || {$crate::lambda::macros::default_flush_tracer(&tracer_provider);})
192 .with_trigger($trigger)
193 )
194 .run()
195 .await
196 }
197 };
198 // tracer_provider and trigger, 2 combinations
199 (
200 $handler:path,
201 trigger = $trigger:expr,
202 telemetry_init = $telemetry_init:path
203 $(, $name:ident() -> $client:ty)*
204 ) => {
205 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $telemetry_init, trigger = $trigger $(,$name() -> $client)*);
206 };
207 (
208 $handler:path,
209 telemetry_init = $telemetry_init:path,
210 trigger = $trigger:expr
211 $(, $name:ident() -> $client:ty)*
212 ) => {
213 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $telemetry_init, trigger = $trigger $(,$name() -> $client)*);
214 };
215 // Only one optional parameter, 2 possibilities
216 (
217 $handler:path,
218 trigger = $trigger:expr
219 $(, $name:ident() -> $client:ty)*
220 ) => {
221 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $crate::init::default_telemetry_init, trigger = $trigger $(,$name() -> $client)*);
222 };
223 (
224 $handler:path,
225 telemetry_init = $telemetry_init:path
226 $(, $name:ident() -> $client:ty)*
227 ) => {
228 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $telemetry_init, trigger = $crate::lambda::layer::OTelFaasTrigger::Http $(,$name() -> $client)*);
229 };
230 // No optional parameter
231 ($handler:path $(, $name:ident() -> $client:ty)*) => {
232 $crate::make_lambda_runtime!(internal $handler, telemetry_init = $crate::init::default_telemetry_init, trigger = $crate::lambda::layer::OTelFaasTrigger::Http $(,$name() -> $client)*);
233 };
234}