Skip to main content

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}