lambda_runtime_api_client/
tracing.rs

1//! This module provides primitives to work with `tracing`
2//! and `tracing-subscriber` in Lambda functions.
3//!
4//! The `tracing` and `tracing-subscriber` crates are re-exported
5//! so you don't have to include them as direct dependencies in
6//! your projects.
7
8use std::{env, str::FromStr};
9
10use subscriber::filter::{EnvFilter, LevelFilter};
11/// Re-export the `tracing` crate to have access to tracing macros
12/// like `info!`, `debug!`, `trace!` and so on.
13pub use tracing::*;
14
15/// Re-export the `tracing-subscriber` crate to build your own subscribers.
16pub use tracing_subscriber as subscriber;
17use tracing_subscriber::fmt::MakeWriter;
18
19const DEFAULT_LOG_LEVEL: &str = "INFO";
20
21/// Initialize `tracing-subscriber` with default logging options.
22///
23/// The default subscriber writes logs to STDOUT in the current context.
24/// If you want to customize the writer, see [`init_default_subscriber_with_writer()`].
25///
26/// This function uses environment variables set with [Lambda's advanced logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/)
27/// if they're configured for your function.
28///
29/// This subscriber sets the logging level based on environment variables:
30///     - if `AWS_LAMBDA_LOG_LEVEL` is set, it takes precedence over any other environment variables.
31///     - if `AWS_LAMBDA_LOG_LEVEL` is not set, check if `RUST_LOG` is set.
32///     - if none of those two variables are set, use `INFO` as the logging level.
33///
34/// The logging format can also be changed based on Lambda's advanced logging controls.
35/// If the `AWS_LAMBDA_LOG_FORMAT` environment variable is set to `JSON`, the log lines will be formatted as json objects,
36/// otherwise they will be formatted with the default tracing format.
37pub fn init_default_subscriber() {
38    init_default_subscriber_with_writer(std::io::stdout);
39}
40
41/// Initialize `tracing-subscriber` with default logging options, and a custom writer.
42///
43/// You might want to avoid writing to STDOUT in the local context via [`init_default_subscriber()`], if you have a high-throughput Lambdas that involve
44/// a lot of async concurrency. Since, writing to STDOUT can briefly block your tokio runtime - ref [tracing #2653](https://github.com/tokio-rs/tracing/issues/2653).
45/// In that case, you might prefer to use [tracing_appender::NonBlocking](https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.NonBlocking.html) instead - particularly if your Lambda is fairly long-running and stays warm.
46/// Though, note that you are then responsible
47/// for ensuring gracefuls shutdown. See [aws-samples/graceful-shutdown-with-aws-lambda](https://github.com/aws-samples/graceful-shutdown-with-aws-lambda) for a complete example.
48///
49/// This function uses environment variables set with [Lambda's advanced logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/)
50/// if they're configured for your function.
51///
52/// This subscriber sets the logging level based on environment variables:
53///     - if `AWS_LAMBDA_LOG_LEVEL` is set, it takes precedence over any other environment variables.
54///     - if `AWS_LAMBDA_LOG_LEVEL` is not set, check if `RUST_LOG` is set.
55///     - if none of those two variables are set, use `INFO` as the logging level.
56///
57/// The logging format can also be changed based on Lambda's advanced logging controls.
58/// If the `AWS_LAMBDA_LOG_FORMAT` environment variable is set to `JSON`, the log lines will be formatted as json objects,
59/// otherwise they will be formatted with the default tracing format.
60pub fn init_default_subscriber_with_writer<Writer>(writer: Writer)
61where
62    Writer: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
63{
64    let log_format = env::var("AWS_LAMBDA_LOG_FORMAT").unwrap_or_default();
65    let log_level_str = env::var("AWS_LAMBDA_LOG_LEVEL").or_else(|_| env::var("RUST_LOG"));
66    let log_level =
67        LevelFilter::from_str(log_level_str.as_deref().unwrap_or(DEFAULT_LOG_LEVEL)).unwrap_or(LevelFilter::INFO);
68
69    let collector = tracing_subscriber::fmt()
70        .with_target(false)
71        .without_time()
72        .with_env_filter(
73            EnvFilter::builder()
74                .with_default_directive(log_level.into())
75                .from_env_lossy(),
76        )
77        .with_writer(writer);
78
79    if log_format.eq_ignore_ascii_case("json") {
80        collector.json().init()
81    } else {
82        collector.init()
83    }
84}