Skip to content

Commit 8ab96dc

Browse files
committed
Implement Lambda's advance logging controls.
Provide a feature that exposes a initialization function for `tracing-subscriber` that sets the right logging controls based on Lambda's configuration. The feature is enabled by default, but it can be disabled if a user doesn't want to use it. Signed-off-by: David Calavera <[email protected]>
1 parent 0d92dd3 commit 8ab96dc

File tree

10 files changed

+98
-8
lines changed

10 files changed

+98
-8
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,24 @@ You can read more about how [cargo lambda watch](https://www.cargo-lambda.info/c
372372

373373
Lambdas can be run and debugged locally using a special [Lambda debug proxy](https://github.com/rimutaka/lambda-debug-proxy) (a non-AWS repo maintained by @rimutaka), which is a Lambda function that forwards incoming requests to one AWS SQS queue and reads responses from another queue. A local proxy running on your development computer reads the queue, calls your Lambda locally and sends back the response. This approach allows debugging of Lambda functions locally while being part of your AWS workflow. The Lambda handler code does not need to be modified between the local and AWS versions.
374374

375+
## Tracing and Logging
376+
377+
The Rust Runtime for Lambda integrates with the (Tracing)[https://tracing.rs] libraries to provide tracing and logging.
378+
379+
By default, the runtime emits `tracing` events that you can collect via `tracing-subscriber`. It also enabled a feature called `tracing` that exposes a default subsriber with sensible options to send logging information to AWS CloudWatch. Follow the next example that shows how to enable the default subscriber:
380+
381+
```rust
382+
use lambda_runtime::{run, service_fn, tracing, Error};
383+
384+
#[tokio::main]
385+
async fn main() -> Result<(), Error> {
386+
tracing::init_default_subscriber();
387+
run(service_fn(|event| tracing::info!(?event))).await
388+
}
389+
```
390+
391+
The subscriber uses `RUST_LOG` as the environment variable to determine the log level for your function. It also uses [Lambda's advance logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/) if they're configured for your function. By default, the log level to emit events is `INFO`.
392+
375393
## AWS event objects
376394

377395
This project includes Lambda event struct definitions, [`aws_lambda_events`](https://crates.io/crates/aws_lambda_events). This crate can be leveraged to provide strongly-typed Lambda event structs. You can create your own custom event objects and their corresponding structs as well.

lambda-extension/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ categories = ["web-programming::http-server"]
1313
keywords = ["AWS", "Lambda", "API"]
1414
readme = "README.md"
1515

16+
[features]
17+
default = ["tracing"]
18+
tracing = ["lambda_runtime_api_client/tracing"]
19+
1620
[dependencies]
1721
async-stream = "0.3"
1822
bytes = { workspace = true }
@@ -24,7 +28,6 @@ hyper-util = { workspace = true }
2428
lambda_runtime_api_client = { version = "0.9", path = "../lambda-runtime-api-client" }
2529
serde = { version = "1", features = ["derive"] }
2630
serde_json = "^1"
27-
tracing = { version = "0.1", features = ["log"] }
2831
tokio = { version = "1.0", features = [
2932
"macros",
3033
"io-util",
@@ -33,3 +36,4 @@ tokio = { version = "1.0", features = [
3336
] }
3437
tokio-stream = "0.1.2"
3538
tower = { version = "0.4", features = ["make", "util"] }
39+
tracing = { version = "0.1", features = ["log"] }

lambda-extension/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ pub use telemetry::*;
2323
/// Include several request builders to interact with the Extension API.
2424
pub mod requests;
2525

26+
/// Utilities to initialize and use `tracing` and `tracing-subscriber` in Lambda Functions.
27+
#[cfg(feature = "tracing")]
28+
pub use lambda_runtime_api_client::tracing;
29+
2630
/// Execute the given events processor
2731
pub async fn run<E>(events_processor: E) -> Result<(), Error>
2832
where

lambda-http/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ categories = ["web-programming::http-server"]
1616
readme = "README.md"
1717

1818
[features]
19-
default = ["apigw_rest", "apigw_http", "apigw_websockets", "alb"]
19+
default = ["apigw_rest", "apigw_http", "apigw_websockets", "alb", "tracing"]
2020
apigw_rest = []
2121
apigw_http = []
2222
apigw_websockets = []
2323
alb = []
2424
pass_through = []
25+
tracing = ["lambda_runtime/tracing"]
2526

2627
[dependencies]
2728
base64 = { workspace = true }

lambda-http/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@
6565
extern crate maplit;
6666

6767
pub use http::{self, Response};
68-
use lambda_runtime::LambdaEvent;
69-
pub use lambda_runtime::{self, service_fn, tower, Context, Error, Service};
68+
/// Utilities to initialize and use `tracing` and `tracing-subscriber` in Lambda Functions.
69+
#[cfg(feature = "tracing")]
70+
pub use lambda_runtime::tracing;
71+
pub use lambda_runtime::{self, service_fn, tower, Context, Error, LambdaEvent, Service};
7072
use request::RequestFuture;
7173
use response::ResponseFuture;
7274

lambda-runtime-api-client/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ categories = ["web-programming::http-server"]
1313
keywords = ["AWS", "Lambda", "API"]
1414
readme = "README.md"
1515

16+
[features]
17+
default = ["tracing"]
18+
tracing = ["dep:tracing-subscriber"]
19+
1620
[dependencies]
1721
bytes = { workspace = true }
1822
futures-channel = { workspace = true }
@@ -30,3 +34,5 @@ hyper-util = { workspace = true, features = [
3034
tower = { workspace = true, features = ["util"] }
3135
tower-service = { workspace = true }
3236
tokio = { version = "1.0", features = ["io-util"] }
37+
tracing = { version = "0.1", features = ["log"] }
38+
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "json", "env-filter"], optional = true }

lambda-runtime-api-client/src/lib.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ mod error;
1717
pub use error::*;
1818
pub mod body;
1919

20+
use tracing::trace;
21+
22+
#[cfg(feature = "tracing")]
23+
pub mod tracing;
24+
2025
/// API client to interact with the AWS Lambda Runtime API.
2126
#[derive(Debug)]
2227
pub struct Client {
@@ -40,8 +45,10 @@ impl Client {
4045
/// Send a given request to the Runtime API.
4146
/// Use the client's base URI to ensure the API endpoint is correct.
4247
pub async fn call(&self, req: Request<body::Body>) -> Result<Response<Incoming>, BoxError> {
43-
let req = self.set_origin(req)?;
44-
self.client.request(req).await.map_err(Into::into)
48+
let request = self.set_origin(req)?;
49+
50+
trace!(?request, "sending request to Lambda Runtime API");
51+
self.client.request(request).await.map_err(Into::into)
4552
}
4653

4754
/// Create a new client with a given base URI and HTTP connector.
@@ -70,6 +77,8 @@ impl Client {
7077
.build()
7178
.map_err(Box::new)?;
7279

80+
trace!(?uri, "setting the Lambda Runtime API origin URI");
81+
7382
parts.uri = uri;
7483
Ok(Request::from_parts(parts, body))
7584
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
8+
use std::{env, str::FromStr};
9+
10+
use subscriber::filter::{EnvFilter, LevelFilter};
11+
/// Re-export the `tracing` crate to have access to tracing macros
12+
/// like `info!`, `debug!`, `trace!` and so on.
13+
pub use tracing::*;
14+
15+
/// Re-export the `tracing-subscriber` crate to build your own subscribers.
16+
pub use tracing_subscriber as subscriber;
17+
18+
/// Initialize `tracing-subscriber` with default options.
19+
/// The subscriber uses `RUST_LOG` as the environment variable to determine the log level for your function.
20+
/// It also uses [Lambda's advance logging controls](https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/)
21+
/// if they're configured for your function.
22+
/// By default, the log level to emit events is `INFO`.
23+
pub fn init_default_subscriber() {
24+
let log_format = env::var("AWS_LAMBDA_LOG_FORMAT").unwrap_or_default();
25+
let log_level = Level::from_str(&env::var("AWS_LAMBDA_LOG_LEVEL").unwrap_or_default()).unwrap_or(Level::INFO);
26+
27+
let collector = tracing_subscriber::fmt()
28+
.with_target(false)
29+
.without_time()
30+
.with_env_filter(
31+
EnvFilter::builder()
32+
.with_default_directive(LevelFilter::from_level(log_level).into())
33+
.from_env_lossy(),
34+
);
35+
36+
if log_format.eq_ignore_ascii_case("json") {
37+
collector.json().init()
38+
} else {
39+
collector.init()
40+
}
41+
}

lambda-runtime/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ keywords = ["AWS", "Lambda", "API"]
1414
readme = "../README.md"
1515

1616
[features]
17-
default = ["simulated"]
18-
simulated = []
17+
default = ["tracing"]
18+
tracing = ["lambda_runtime_api_client/tracing"]
1919

2020
[dependencies]
2121
async-stream = "0.3"

lambda-runtime/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ mod deserializer;
3030
mod requests;
3131
/// Utilities for Lambda Streaming functions.
3232
pub mod streaming;
33+
34+
/// Utilities to initialize and use `tracing` and `tracing-subscriber` in Lambda Functions.
35+
#[cfg(feature = "tracing")]
36+
pub use lambda_runtime_api_client::tracing;
37+
3338
/// Types available to a Lambda function.
3439
mod types;
3540

0 commit comments

Comments
 (0)