Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced logging controls #830

Merged
merged 6 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,24 @@ You can read more about how [cargo lambda watch](https://www.cargo-lambda.info/c

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.

## Tracing and Logging

The Rust Runtime for Lambda integrates with the (Tracing)[https://tracing.rs] libraries to provide tracing and logging.

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:

```rust
use lambda_runtime::{run, service_fn, tracing, Error};

#[tokio::main]
async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();
run(service_fn(|event| tracing::info!(?event))).await
}
```

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`.

## AWS event objects

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,14 @@ name = "consumer"
version = "0.1.0"
edition = "2021"


[dependencies]
#tracing
tracing = "0.1.40"
tracing-subscriber = "0.3.17"

#aws dependencies
aws-sdk-config = "0.35.0"
aws-sdk-sqs = "0.35.0"
aws_lambda_events = { version = "0.11.1", features = ["sqs"], default-features = false }

#lambda runtime
lambda_runtime = "0.8.1"
lambda_runtime = { path = "../../../lambda-runtime" }
tokio = { version = "1", features = ["macros"] }

#shared lib
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use aws_lambda_events::event::sqs::SqsEventObj;
use lambda_runtime::{service_fn, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use pizza_lib::Pizza;

#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.with_ansi(false)
.without_time()
.init();
tracing::init_default_subscriber();
let func = service_fn(func);
lambda_runtime::run(func).await?;
Ok(())
Expand All @@ -18,7 +13,7 @@ async fn main() -> Result<(), Error> {
async fn func(event: LambdaEvent<SqsEventObj<Pizza>>) -> Result<(), Error> {
for record in event.payload.records.iter() {
let pizza = &record.body;
println!("Pizza name: {} with toppings: {:?}", pizza.name, pizza.toppings);
tracing::info!(name = pizza.name, toppings = ?pizza.toppings, "pizza order received");
}
Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@ edition = "2021"
env = { "QUEUE_URL" = "https://changeMe" }

[dependencies]
#tracing
tracing = "0.1.40"
tracing-subscriber = "0.3.17"

#aws dependencies
aws-config = "0.57.1"
aws-sdk-config = "0.35.0"
aws-sdk-sqs = "0.35.0"

#lambda runtime
lambda_runtime = "0.8.1"
lambda_runtime = { path = "../../../lambda-runtime" }
serde_json = "1.0.108"
tokio = { version = "1", features = ["macros"] }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lambda_runtime::{service_fn, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use pizza_lib::Pizza;
use serde_json::{json, Value};

Expand All @@ -15,12 +15,7 @@ impl SQSManager {

#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.with_ansi(false)
.without_time()
.init();
tracing::init_default_subscriber();

// read the queue url from the environment
let queue_url = std::env::var("QUEUE_URL").expect("could not read QUEUE_URL");
Expand All @@ -31,9 +26,7 @@ async fn main() -> Result<(), Error> {
let sqs_manager_ref = &sqs_manager;

// no need to create a SQS Client for each incoming request, let's use a shared state
let handler_func_closure = |event: LambdaEvent<Value>| async move {
process_event(event, sqs_manager_ref).await
};
let handler_func_closure = |event: LambdaEvent<Value>| async move { process_event(event, sqs_manager_ref).await };
lambda_runtime::run(service_fn(handler_func_closure)).await?;
Ok(())
}
Expand Down
2 changes: 0 additions & 2 deletions examples/advanced-sqs-partial-batch-failures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ aws_lambda_events = { path = "../../lambda-events" }
lambda_runtime = { path = "../../lambda-runtime" }
tokio = { version = "1", features = ["macros"] }
futures = "0.3"
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
15 changes: 6 additions & 9 deletions examples/advanced-sqs-partial-batch-failures/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ use aws_lambda_events::{
sqs::{BatchItemFailure, SqsBatchResponse, SqsMessageObj},
};
use futures::Future;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use lambda_runtime::{
run, service_fn,
tracing::{self, Instrument},
Error, LambdaEvent,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use tracing::Instrument;

/// [To customize] Your object definition, sent to the SQS queue triggering this lambda.
#[derive(Deserialize, Serialize)]
Expand All @@ -29,13 +32,7 @@ async fn data_handler(data: Data) -> Result<(), Error> {
#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

run_sqs_partial_batch_failure(data_handler).await
}
Expand Down
11 changes: 0 additions & 11 deletions examples/basic-error-handling/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,9 @@ name = "error-handling"
version = "0.1.0"
edition = "2021"


# Use cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to manage dependencies.
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.

[dependencies]
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.136"
serde_json = "1.0.81"
simple-error = "0.2.3"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }


10 changes: 2 additions & 8 deletions examples/basic-error-handling/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// See https://github.com/awslabs/aws-lambda-rust-runtime for more info on Rust runtime for AWS Lambda
use lambda_runtime::{service_fn, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::fs::File;
Expand Down Expand Up @@ -50,13 +50,7 @@ impl std::fmt::Display for CustomError {
#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

// call the actual handler of the request
let func = service_fn(func);
Expand Down
7 changes: 3 additions & 4 deletions examples/basic-lambda-external-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ edition = "2021"
[dependencies]
async-channel = "1.8.0"
futures-lite = "1.13.0"
lambda_runtime = "0.8.0"
lambda_runtime_api_client = "0.8.0"
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.163"
tokio = "1.28.2"

[dev-dependencies]
tokio-test = "0.4.2"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
10 changes: 2 additions & 8 deletions examples/basic-lambda-external-runtime/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{io, thread};

use futures_lite::future;
use lambda_runtime::{service_fn, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use tokio::runtime::Builder;

Expand All @@ -25,13 +25,7 @@ struct Response {

fn main() -> Result<(), io::Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

// Create a channel used to send and receive outputs from our lambda handler. Realistically, this would be either an unbounded channel
// or a bounded channel with a higher capacity as needed.
Expand Down
11 changes: 2 additions & 9 deletions examples/basic-lambda/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@ name = "basic-lambda"
version = "0.1.0"
edition = "2021"


# Use cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to manage dependencies.
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.

[dependencies]
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.136"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }

[dev-dependencies]
tokio-test = "0.4.2"
10 changes: 2 additions & 8 deletions examples/basic-lambda/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This example requires the following input to succeed:
// { "command": "do something" }

use lambda_runtime::{service_fn, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use serde::{Deserialize, Serialize};

/// This is also a made-up example. Requests come into the runtime as unicode
Expand All @@ -25,13 +25,7 @@ struct Response {
#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

let func = service_fn(my_handler);
lambda_runtime::run(func).await?;
Expand Down
2 changes: 0 additions & 2 deletions examples/basic-s3-object-lambda-thumbnail/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ aws_lambda_events = "0.8.3"
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
aws-config = "0.55.3"
aws-sdk-s3 = "0.28.0"
thumbnailer = "0.4.0"
Expand Down
10 changes: 2 additions & 8 deletions examples/basic-s3-object-lambda-thumbnail/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::error;

use aws_lambda_events::s3::object_lambda::{GetObjectContext, S3ObjectLambdaEvent};
use aws_sdk_s3::Client as S3Client;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};
use s3::{GetFile, SendFile};

mod s3;
Expand Down Expand Up @@ -57,13 +57,7 @@ fn get_thumbnail(vec: Vec<u8>, size: u32) -> Vec<u8> {
#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

let shared_config = aws_config::load_from_env().await;
let client = S3Client::new(&shared_config);
Expand Down
1 change: 1 addition & 0 deletions examples/basic-s3-object-lambda-thumbnail/src/s3.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use async_trait::async_trait;
use aws_sdk_s3::{operation::write_get_object_response::WriteGetObjectResponseError, Client as S3Client};
use aws_smithy_http::{byte_stream::ByteStream, result::SdkError};
use lambda_runtime::tracing;
use std::{error, io::Read};

pub trait GetFile {
Expand Down
2 changes: 0 additions & 2 deletions examples/basic-s3-thumbnail/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ aws_lambda_events = { path = "../../lambda-events" }
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", default-features = false, features = ["ansi", "fmt"] }
aws-config = "0.55"
aws-smithy-http = "0.55.3"
aws-sdk-s3 = "0.28"
Expand Down
10 changes: 2 additions & 8 deletions examples/basic-s3-thumbnail/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use aws_lambda_events::{event::s3::S3Event, s3::S3EventRecord};
use aws_sdk_s3::Client as S3Client;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};
use s3::{GetFile, PutFile};

mod s3;
Expand Down Expand Up @@ -109,13 +109,7 @@ fn get_thumbnail(vec: Vec<u8>, size: u32) -> Result<Vec<u8>, String> {
#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

let shared_config = aws_config::load_from_env().await;
let client = S3Client::new(&shared_config);
Expand Down
1 change: 1 addition & 0 deletions examples/basic-s3-thumbnail/src/s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use async_trait::async_trait;
use aws_sdk_s3::operation::get_object::GetObjectError;
use aws_sdk_s3::Client as S3Client;
use aws_smithy_http::byte_stream::ByteStream;
use lambda_runtime::tracing;

#[async_trait]
pub trait GetFile {
Expand Down
2 changes: 0 additions & 2 deletions examples/basic-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ aws-sdk-s3 = "0.24"
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.136"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }

[dev-dependencies]
mockall = "0.11.3"
Expand Down
10 changes: 2 additions & 8 deletions examples/basic-sdk/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use async_trait::async_trait;
use aws_sdk_s3::{output::ListObjectsV2Output, Client as S3Client};
use lambda_runtime::{service_fn, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use serde::{Deserialize, Serialize};

/// The request defines what bucket to list
Expand Down Expand Up @@ -34,13 +34,7 @@ impl ListObjects for S3Client {
#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
tracing::init_default_subscriber();

let shared_config = aws_config::load_from_env().await;
let client = S3Client::new(&shared_config);
Expand Down
Loading
Loading