diff --git a/Cargo.lock b/Cargo.lock index 2d9e9c5..451d2f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1231,6 +1231,8 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tracing", + "tracing-subscriber", "uuid", ] @@ -1246,6 +1248,7 @@ dependencies = [ "quote", "serde", "syn 2.0.52", + "tracing", "trybuild", ] @@ -1368,6 +1371,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -1405,6 +1418,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460b31760eb56186dc8006c41b807a59c25dda5aad08eb90c55d84d19e8aff40" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p256" version = "0.11.0" @@ -1796,6 +1815,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "socket2" version = "0.5.5" @@ -2110,6 +2135,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -2127,14 +2163,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", + "nu-ansi-term", "once_cell", "regex", "serde", "serde_json", "sharded-slab", + "smallvec", "thread_local", "tracing", "tracing-core", + "tracing-log", "tracing-serde", ] @@ -2290,9 +2329,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" -version = "0.3.0" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -2300,15 +2339,15 @@ dependencies = [ [[package]] name = "winapi-i686-pc-windows-gnu" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "wincolor" diff --git a/Cargo.toml b/Cargo.toml index cde9255..8a56b8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,13 +28,20 @@ tokio = { version = "1", features = ["macros"] } lambda_runtime = "1.0" aws-config = { version = "1.5", features = ["behavior-version-latest"] } aws-smithy-types = "1.3" -log = "0.4" -env_logger = "0.11" thiserror = "1.0" uuid = { version = "1.11", features = ["v4"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +# Default Lambda logging is based on log/env_logger +log = "0.4" +env_logger = "0.11" + +# Alternatively, a feature allow the use of tracing/tracing-subscriber +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] } + + # Proc-macro crate dependencies syn = { version = "2.0", default-features = false, features = [ "parsing", diff --git a/lambda-appsync-proc/Cargo.toml b/lambda-appsync-proc/Cargo.toml index a8e5585..6889ac1 100644 --- a/lambda-appsync-proc/Cargo.toml +++ b/lambda-appsync-proc/Cargo.toml @@ -21,8 +21,15 @@ proc-macro2 = { workspace = true } graphql-parser = { workspace = true } [dev-dependencies] -lambda-appsync = { path = "../lambda-appsync" } +lambda-appsync = { path = "../lambda-appsync", default-features = false, features = ["log", "env_logger", "tracing"] } +tracing = { workspace = true } aws-sdk-s3 = { workspace = true } aws-sdk-dynamodb = { workspace = true } serde = { workspace = true } trybuild = { workspace = true } + +[features] +default = ["log", "env_logger", "tracing"] +log = [] +env_logger = [] +tracing = [] diff --git a/lambda-appsync-proc/src/appsync_lambda_main/graphql.rs b/lambda-appsync-proc/src/appsync_lambda_main/graphql.rs index e33696f..29c8bb0 100644 --- a/lambda-appsync-proc/src/appsync_lambda_main/graphql.rs +++ b/lambda-appsync-proc/src/appsync_lambda_main/graphql.rs @@ -971,7 +971,16 @@ impl GraphQLSchema { let subscription_field_execute_match_arm = self .subscriptions .execute_match_arm_iter(OperationKind::Subscription); + let span = current_span(); + + #[allow(unused_mut)] + let mut log_lines = proc_macro2::TokenStream::new(); + #[cfg(feature = "log")] + log_lines.extend(quote_spanned! {span=> + ::lambda_appsync::log::error!("{e}"); + }); + tokens.extend(quote_spanned! {span=> impl Operation { async fn execute(self, @@ -980,7 +989,7 @@ impl GraphQLSchema { match self._execute(event).await { ::core::result::Result::Ok(v) => v.into(), ::core::result::Result::Err(e) => { - ::lambda_appsync::log::error!("{e}"); + #log_lines e.into() } } diff --git a/lambda-appsync-proc/src/appsync_lambda_main/mod.rs b/lambda-appsync-proc/src/appsync_lambda_main/mod.rs index 3bff57f..0958438 100644 --- a/lambda-appsync-proc/src/appsync_lambda_main/mod.rs +++ b/lambda-appsync-proc/src/appsync_lambda_main/mod.rs @@ -70,6 +70,9 @@ enum OptionalParameter { ExcludeAppsyncOperations(bool), OnlyAppsyncOperations(bool), Hook(Ident), + LogInit(Ident), + #[cfg(feature = "log")] + EventLogging(bool), TypeOverride(TypeOverride), NameOverride(NameOverride), } @@ -94,6 +97,9 @@ impl Parse for OptionalParameter { input.parse::()?.value(), )), "hook" => Ok(Self::Hook(input.parse()?)), + "log_init" => Ok(Self::LogInit(input.parse()?)), + #[cfg(feature = "log")] + "event_logging" => Ok(Self::EventLogging(input.parse::()?.value())), "type_override" => Ok(Self::TypeOverride(input.parse()?)), "name_override" => Ok(Self::NameOverride(input.parse()?)), // Deprecated options @@ -148,6 +154,9 @@ struct OptionalParameters { appsync_operations: bool, lambda_handler: bool, hook: Option, + log_init: Option, + #[cfg(feature = "log")] + event_logging: bool, tos: TypeOverrides, nos: NameOverrides, } @@ -159,6 +168,9 @@ impl Default for OptionalParameters { appsync_operations: true, lambda_handler: true, hook: None, + log_init: None, + #[cfg(feature = "log")] + event_logging: false, tos: TypeOverrides::new(), nos: NameOverrides::new(), } @@ -189,6 +201,13 @@ impl OptionalParameters { OptionalParameter::Hook(ident) => { self.hook.replace(ident); } + OptionalParameter::LogInit(ident) => { + self.log_init.replace(ident); + } + #[cfg(feature = "log")] + OptionalParameter::EventLogging(b) => { + self.event_logging = b; + } OptionalParameter::TypeOverride(to) => { // Retrieve the entry corresponding to `Type.field` let to_field_entry = self @@ -305,6 +324,19 @@ impl Parse for AppsyncLambdaMain { impl AppsyncLambdaMain { fn appsync_event_handler(&self, tokens: &mut TokenStream2) { + #[allow(unused_mut)] + let mut log_lines = proc_macro2::TokenStream::new(); + #[cfg(feature = "log")] + if self.options.event_logging { + log_lines.extend(quote! { + ::lambda_appsync::log::debug!("event={event:?}"); + }); + } + #[cfg(feature = "log")] + log_lines.extend(quote! { + ::lambda_appsync::log::info!("operation={:?}", event.info.operation); + }); + let call_hook = if let Some(ref hook) = self.options.hook { quote_spanned! {hook.span()=> mod _check_sig { @@ -326,10 +358,15 @@ impl AppsyncLambdaMain { } else { quote! {} }; + + #[cfg(feature = "tracing")] + tokens.extend(quote! { + #[::lambda_appsync::tracing::instrument(skip(event), fields(operation = ?event.info.operation))] + }); + tokens.extend(quote! { async fn appsync_handler(event: ::lambda_appsync::AppsyncEvent) -> ::lambda_appsync::AppsyncResponse { - ::lambda_appsync::log::info!("event={event:?}"); - ::lambda_appsync::log::info!("operation={:?}", event.info.operation); + #log_lines #call_hook @@ -357,14 +394,40 @@ impl AppsyncLambdaMain { } } - fn lambda_main(&self, tokens: &mut TokenStream2) { - let (config_init, config_getter) = if !self.aws_clients.is_empty() { - (AWSClient::aws_config_init(), AWSClient::aws_config_getter()) - } else { - (TokenStream2::new(), TokenStream2::new()) - }; - let aws_client_getters = self.aws_clients.iter().map(|ac| ac.aws_client_getter()); + #[allow(dead_code)] + fn default_env_logger_init() -> TokenStream2 { + quote! { + ::lambda_appsync::env_logger::Builder::from_env( + ::lambda_appsync::env_logger::Env::default() + .default_filter_or("info,tracing::span=warn") + .default_write_style_or("never"), + ) + .format_timestamp_micros() + .init(); + } + } + + #[allow(dead_code)] + fn default_tracing_init() -> TokenStream2 { + quote! { + ::lambda_appsync::tracing_subscriber::fmt() + .json() + .with_env_filter( + ::lambda_appsync::tracing_subscriber::EnvFilter::from_default_env() + .add_directive(tracing::Level::INFO.into()), + ) + // this needs to be set to remove duplicated information in the log. + .with_current_span(false) + // this needs to be set to false, otherwise ANSI color codes will + // show up in a confusing manner in CloudWatch logs. + .with_ansi(false) + // remove the name of the function from every log entry + .with_target(false) + .init(); + } + } + fn lambda_function_handler(&self, tokens: &mut TokenStream2) { let (appsync_handler, ret_type) = if self.options.batch { ( format_ident!("appsync_batch_handler"), @@ -377,29 +440,81 @@ impl AppsyncLambdaMain { ) }; + #[cfg(feature = "tracing")] + tokens.extend(quote! { + #[::lambda_appsync::tracing::instrument(skip(event), fields(req_id = %event.context.request_id))] + }); + + #[allow(unused_mut)] + let mut log_lines = proc_macro2::TokenStream::new(); + #[cfg(feature = "log")] + if self.options.event_logging { + log_lines.extend(quote! { + ::lambda_appsync::log::debug!("{}", ::lambda_appsync::serde_json::json!(event.payload)); + }); + } + tokens.extend(quote! { async fn function_handler( event: ::lambda_appsync::lambda_runtime::LambdaEvent<::lambda_appsync::serde_json::Value>, ) -> ::core::result::Result<#ret_type, ::lambda_appsync::lambda_runtime::Error> { - ::lambda_appsync::log::debug!("{event:?}"); - ::lambda_appsync::log::info!("{}", ::lambda_appsync::serde_json::json!(event.payload)); + #log_lines Ok(#appsync_handler(::lambda_appsync::serde_json::from_value(event.payload)?).await) } + }); + } + + fn lambda_main(&self, tokens: &mut TokenStream2) { + let (config_init, config_getter) = if !self.aws_clients.is_empty() { + (AWSClient::aws_config_init(), AWSClient::aws_config_getter()) + } else { + (TokenStream2::new(), TokenStream2::new()) + }; + let aws_client_getters = self.aws_clients.iter().map(|ac| ac.aws_client_getter()); + + let log_init = if let Some(ref log_init) = self.options.log_init { + quote_spanned! {log_init.span()=> + mod _check_sig { + #[inline(always)] + pub(super) fn call_log_init ()>(f: F) {f()} + } + _check_sig::call_log_init(#log_init); + } + } else { + #[allow(unused_mut)] + let mut default_log_init = proc_macro2::TokenStream::new(); + #[cfg(feature = "env_logger")] + default_log_init.extend(Self::default_env_logger_init()); + // The code initializing tracing fails if the env_logger initialization already happened + #[cfg(all(feature = "tracing", not(any(feature = "env_logger"))))] + default_log_init.extend(Self::default_tracing_init()); + // Future default inits can be inserted here like that for feature "fastrace" (for example): + // #[cfg(all(feature = "fastrace", not(any(feature = "env_logger", feature = "tracing"))))] + // default_log_init.extend(Self::default_fastrace_init()); + default_log_init + }; + + #[allow(unused_mut)] + let mut bring_in_scope = TokenStream2::new(); + bring_in_scope.extend(quote! { + use ::lambda_appsync::tokio; + }); + #[cfg(feature = "tracing")] + bring_in_scope.extend(quote! { + use ::lambda_appsync::tracing; + }); + + tokens.extend(quote! { #config_getter #(#aws_client_getters)* - use ::lambda_appsync::tokio; + #bring_in_scope + #[tokio::main] async fn main() -> ::core::result::Result<(), ::lambda_appsync::lambda_runtime::Error> { - ::lambda_appsync::env_logger::Builder::from_env( - ::lambda_appsync::env_logger::Env::default() - .default_filter_or("info,tracing::span=warn") - .default_write_style_or("never"), - ) - .format_timestamp_micros() - .init(); + #log_init #config_init @@ -419,6 +534,7 @@ impl ToTokens for AppsyncLambdaMain { } if self.options.lambda_handler { self.appsync_event_handler(tokens); + self.lambda_function_handler(tokens); self.lambda_main(tokens); } } diff --git a/lambda-appsync-proc/src/lib.rs b/lambda-appsync-proc/src/lib.rs index 6858753..75a113d 100644 --- a/lambda-appsync-proc/src/lib.rs +++ b/lambda-appsync-proc/src/lib.rs @@ -29,6 +29,9 @@ use proc_macro::TokenStream; /// /// - `batch = bool`: Enable/disable batch request handling (default: true) /// - `hook = fn_name`: Add a custom hook function for request validation/auth +/// - `log_init = fn_name`: Use a custom log initialization function instead of the default one +/// - (feature: `log`) `event_logging = bool`: If true, the macro will generate code to dump the +/// lambda payload JSON as well as parsed `AppsyncEvent`s in the logs at debug level (default: `false`) /// - `exclude_lambda_handler = bool`: Skip generation of Lambda handler code /// - `only_lambda_handler = bool`: Only generate Lambda handler code /// - `exclude_appsync_types = bool`: Skip generation of GraphQL type definitions @@ -206,6 +209,68 @@ use proc_macro::TokenStream; /// Note that when using `name_override`, the macro does not automatically change the case: /// you are responsible to provide the appropriate casing or Clippy will complain. /// +/// ## Use a custom log initialization function: +/// +/// ### Feature `env_logger` (default) +/// +/// By default, `lambda_appsync` exposes and uses `log` and `env_logger`. You can override the +/// initialization code if you wish: +/// +/// ```no_run +/// # mod sub { +/// // This is in fact equivalent to the default initialization code +/// fn log_init_fct() { +/// use lambda_appsync::env_logger; +/// env_logger::Builder::from_env( +/// env_logger::Env::default() +/// // Default log level is info, expect tracing::span is warn +/// .default_filter_or("info,tracing::span=warn") +/// .default_write_style_or("never"), +/// ) +/// // Format timestamps with microseconds +/// .format_timestamp_micros() +/// .init(); +/// } +/// lambda_appsync::appsync_lambda_main!( +/// "schema.graphql", +/// log_init = log_init_fct +/// ); +/// # } +/// # fn main() {} +/// ``` +/// +/// ### Feature `tracing` +/// +/// Alternatively, you can use the `tracing` feature so `lambda_appsync` exposes and uses `log`, `tracing` and `tracing-subscriber` +/// +/// ```no_run +/// # mod sub { +/// // This is in fact equivalent to the default initialization code +/// fn tracing_init_fct() { +/// use lambda_appsync::{tracing, tracing_subscriber}; +/// tracing_subscriber::fmt() +/// .json() +/// .with_env_filter( +/// tracing_subscriber::EnvFilter::from_default_env() +/// .add_directive(tracing::Level::INFO.into()), +/// ) +/// // this needs to be set to remove duplicated information in the log. +/// .with_current_span(false) +/// // this needs to be set to false, otherwise ANSI color codes will +/// // show up in a confusing manner in CloudWatch logs. +/// .with_ansi(false) +/// // remove the name of the function from every log entry +/// .with_target(false) +/// .init(); +/// } +/// lambda_appsync::appsync_lambda_main!( +/// "schema.graphql", +/// log_init = tracing_init_fct +/// ); +/// # } +/// # fn main() {} +/// ``` +/// /// ## Disable batch processing: /// ```no_run /// # mod sub { diff --git a/lambda-appsync-proc/tests/fail/invalid_log_init_args.rs b/lambda-appsync-proc/tests/fail/invalid_log_init_args.rs new file mode 100644 index 0000000..17fb867 --- /dev/null +++ b/lambda-appsync-proc/tests/fail/invalid_log_init_args.rs @@ -0,0 +1,8 @@ +mod no_run { + fn custom_log_init(_arg: String) { + // Here would go custom initialization code + } + use lambda_appsync::appsync_lambda_main; + appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); +} +fn main() {} diff --git a/lambda-appsync-proc/tests/fail/invalid_log_init_args.stderr b/lambda-appsync-proc/tests/fail/invalid_log_init_args.stderr new file mode 100644 index 0000000..d001789 --- /dev/null +++ b/lambda-appsync-proc/tests/fail/invalid_log_init_args.stderr @@ -0,0 +1,14 @@ +error[E0593]: function is expected to take 0 arguments, but it takes 1 argument + --> tests/fail/invalid_log_init_args.rs:6:67 + | +2 | fn custom_log_init(_arg: String) { + | -------------------------------- takes 1 argument +... +6 | appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); + | ^^^^^^^^^^^^^^^ expected function that takes 0 arguments + | +note: required by a bound in `call_log_init` + --> tests/fail/invalid_log_init_args.rs:6:67 + | +6 | appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); + | ^^^^^^^^^^^^^^^ required by this bound in `call_log_init` diff --git a/lambda-appsync-proc/tests/fail/invalid_log_init_ret.rs b/lambda-appsync-proc/tests/fail/invalid_log_init_ret.rs new file mode 100644 index 0000000..c21e93d --- /dev/null +++ b/lambda-appsync-proc/tests/fail/invalid_log_init_ret.rs @@ -0,0 +1,9 @@ +mod no_run { + fn custom_log_init() -> &'static str { + // Here would go custom initialization code + "some value" + } + use lambda_appsync::appsync_lambda_main; + appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); +} +fn main() {} diff --git a/lambda-appsync-proc/tests/fail/invalid_log_init_ret.stderr b/lambda-appsync-proc/tests/fail/invalid_log_init_ret.stderr new file mode 100644 index 0000000..d7a6c50 --- /dev/null +++ b/lambda-appsync-proc/tests/fail/invalid_log_init_ret.stderr @@ -0,0 +1,11 @@ +error[E0271]: expected `custom_log_init` to be a fn item that returns `()`, but it returns `&str` + --> tests/fail/invalid_log_init_ret.rs:7:67 + | +7 | appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); + | ^^^^^^^^^^^^^^^ expected `()`, found `&str` + | +note: required by a bound in `call_log_init` + --> tests/fail/invalid_log_init_ret.rs:7:67 + | +7 | appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); + | ^^^^^^^^^^^^^^^ required by this bound in `call_log_init` diff --git a/lambda-appsync-proc/tests/pass/event_logging_option.rs b/lambda-appsync-proc/tests/pass/event_logging_option.rs new file mode 100644 index 0000000..86ab463 --- /dev/null +++ b/lambda-appsync-proc/tests/pass/event_logging_option.rs @@ -0,0 +1,6 @@ +mod no_run { + // Test with event_logging enabled + lambda_appsync::appsync_lambda_main!("../../../../schema.graphql", event_logging = true); +} + +fn main() {} diff --git a/lambda-appsync-proc/tests/pass/log_init.rs b/lambda-appsync-proc/tests/pass/log_init.rs new file mode 100644 index 0000000..02257ac --- /dev/null +++ b/lambda-appsync-proc/tests/pass/log_init.rs @@ -0,0 +1,9 @@ +mod no_run { + fn custom_log_init() { + // Here would go custom initialization code + } + use lambda_appsync::appsync_lambda_main; + appsync_lambda_main!("../../../../schema.graphql", log_init = custom_log_init); +} + +fn main() {} diff --git a/lambda-appsync/Cargo.toml b/lambda-appsync/Cargo.toml index 71e076b..52b287d 100644 --- a/lambda-appsync/Cargo.toml +++ b/lambda-appsync/Cargo.toml @@ -13,17 +13,29 @@ keywords.workspace = true categories.workspace = true [dependencies] -lambda-appsync-proc = { path = "../lambda-appsync-proc" } +lambda-appsync-proc = { path = "../lambda-appsync-proc", default-features = false } tokio = { workspace = true } lambda_runtime = { workspace = true } aws-config = { workspace = true } aws-smithy-types = { workspace = true } -log = { workspace = true } -env_logger = { workspace = true } thiserror = { workspace = true } uuid = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +# For feature env_logger (defautl) +log = { workspace = true, optional = true } +env_logger = { workspace = true, optional = true } + +# For feature tracing +tracing = { workspace = true, optional = true } +tracing-subscriber = { workspace = true, optional = true } + [dev-dependencies] aws-sdk-dynamodb = { workspace = true } + +[features] +default = ["env_logger"] +env_logger = ["log", "dep:env_logger", "lambda-appsync-proc/env_logger"] +tracing = ["log", "dep:tracing", "dep:tracing-subscriber", "lambda-appsync-proc/tracing"] +log = ["dep:log", "lambda-appsync-proc/log"] diff --git a/lambda-appsync/src/lib.rs b/lambda-appsync/src/lib.rs index 3d25981..b754279 100644 --- a/lambda-appsync/src/lib.rs +++ b/lambda-appsync/src/lib.rs @@ -103,13 +103,22 @@ pub use lambda_appsync_proc::appsync_operation; // Re-export crates that are mandatory for the proc_macro to succeed pub use aws_config; -pub use env_logger; pub use lambda_runtime; -pub use log; pub use serde; pub use serde_json; pub use tokio; +#[cfg(feature = "log")] +pub use log; + +#[cfg(feature = "env_logger")] +pub use env_logger; + +#[cfg(feature = "tracing")] +pub use tracing; +#[cfg(feature = "tracing")] +pub use tracing_subscriber; + /// Authorization strategy for AppSync operations. /// /// It determines whether operations are allowed or denied based on the