Rust Lambda 関数のログ記録とモニタリング - AWS Lambda

Rust Lambda 関数のログ記録とモニタリング

注記

Rust ランタイムクライアント」は実験的なパッケージです。これは変更される可能性があり、評価のみを目的としています。

AWS Lambda は、ユーザーに代わって Lambda 関数を自動的にモニタリングし、Amazon CloudWatch にログを送信します。Lambda 関数には、関数のインスタンスごとに CloudWatch Logs ロググループとログストリームが用意されています。Lambda ランタイム環境は、各呼び出しの詳細をログストリームに送信し、関数のコードからのログやその他の出力を中継します。詳細については、「AWS Lambda での Amazon CloudWatch Logs の使用」を参照してください。このページでは、Lambda 関数のコードからログ出力を生成する方法について説明します。

ログを書く関数の作成

関数コードからログを出力するには、println! マクロなどの stdout または stderr に書き込む任意のログ記録ライブラリを使用できます。次の例は、println!を使用して、関数ハンドラーの開始時と終了前にメッセージを印刷します。

use lambda_runtime::{service_fn, LambdaEvent, Error}; use serde_json::{json, Value}; async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> { println!("Rust function invoked"); let payload = event.payload; let first_name = payload["firstName"].as_str().unwrap_or("world"); println!("Rust function responds to {}", &first_name); Ok(json!({ "message": format!("Hello, {first_name}!") })) } #[tokio::main] async fn main() -> Result<(), Error> { lambda_runtime::run(service_fn(handler)).await }

トレーシングクレートによる高度なログ記録機能の実装

トレーシングは、構造化されたイベントベースの診断情報を収集するために Rust プログラムをインストルメントするためのフレームワークです。このフレームワークには、構造化された JSON ログメッセージの作成など、ロギング出力レベルと形式をカスタマイズするユーティリティが用意されています。このフレームワークを使用するには、関数ハンドラーを実装する前に subscriber を初期化する必要があります。次に、debuginfoerror などのトレースマクロを使用して、各シナリオに必要なログのレベルを指定できます。

例 — トレーシングクレートの使用

次の点に注意してください。

  • tracing_subscriber::fmt().json(): このオプションが含まれる場合、ログは JSON でフォーマットされます。このオプションを使用するには、tracing-subscriberjson 機能を依存関係に含める必要があります (例: tracing-subscriber = { version = "0.3.11", features = ["json"] })。

  • #[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))]: このアノテーションは、ハンドラが呼び出されるたびにスパンを生成します。このスパンは、各ログ行にリクエスト ID を追加します。

  • { %first_name }: このコンストラクトは、使用されているログ行に first_name フィールドを追加します。このフィールドの値は、同じ名前の変数に対応します。

use lambda_runtime::{service_fn, Error, LambdaEvent}; use serde_json::{json, Value}; #[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))] async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> { tracing::info!("Rust function invoked"); let payload = event.payload; let first_name = payload["firstName"].as_str().unwrap_or("world"); tracing::info!({ %first_name }, "Rust function responds to event"); Ok(json!({ "message": format!("Hello, {first_name}!") })) } #[tokio::main] async fn main() -> Result<(), Error> { tracing_subscriber::fmt().json() .with_max_level(tracing::Level::INFO) // 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) // disabling time is handy because CloudWatch will add the ingestion time. .without_time() // remove the name of the function from every log entry .with_target(false) .init(); lambda_runtime::run(service_fn(handler)).await }

この Rust 関数が呼び出されると、次のようなログ行が 2 行出力されます。

{"level":"INFO","fields":{"message":"Rust function invoked"},"spans":[{"req_id":"45daaaa7-1a72-470c-9a62-e79860044bb5","name":"handler"}]} {"level":"INFO","fields":{"message":"Rust function responds to event","first_name":"David"},"spans":[{"req_id":"45daaaa7-1a72-470c-9a62-e79860044bb5","name":"handler"}]}