TL;DR
構造化ログを出力するための Rust のクレート。オブザーバビリティの重要な要素であるスパンやイベントを発行できる。
なぜ必要か
スパンやイベントを発行すると、アプリケーションがどのような処理を行い、どのようなデータを返したかを追跡できる。非同期処理にも対応している。Tokio と同じチームが開発しているが、async-std や smol など、Tokio 以外の非同期ランタイムでも使える。
仕組み
計装するには Subscriber の実装が必要で、基本的には公式が提供する tracing-subscriber を使う。Subscriber は、スパンの状態を管理しながらイベントを記録し、扱うログレベルや出力形式、出力先を制御する。開発者が自由に実装できるよう、別のクレートとして提供されている。出力形式は JSON などから選べ、形式そのものをカスタマイズすることもできる。
tracing::instrument
#[tracing::instrument] 属性マクロを関数に付けると、その関数のスパンが作られる。これにより、関数の中で起きたイベントを 1 つのまとまりとして扱える。
例
構造化ログ(JSON)を出力する
tracing-subscriber に json フィーチャを追加する。
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] }初期設定で json メソッドを呼び出す。
let subscriber = tracing_subscriber::fmt::layer()
// ...
.json();出力されたログは次のとおり。
{
"timestamp": "2024-10-14T02:18:41.796486Z",
"level": "INFO",
"fields": { "message": "Listening on 127.0.0.1:8080" },
"filename": "src/bin/app.rs",
"line_number": 87
}ローカルでは構造化ログが読みにくいので、本番環境でのみ構造化ログにすることもできる。
let subscriber = tracing_subscriber::fmt::layer()
.with_file(true)
.with_line_number(true)
.with_target(false);
// リリースビルドでのみ構造化ログを出力する
#[cfg(not(debug_assertions))]
let subscriber = subscriber.json();ハンドラ関数にスパンを付ける
skip でスパンに含めない引数を指定し、fields でスパンに含める情報を加工している。
#[tracing::instrument(
skip(_user, registry),
fields(
user_id = %_user.user.id.to_string()
)
)]
pub async fn show_book(
_user: AuthorizedUser,
State(registry): State<AppRegistry>,
Path(book_id): Path<BookId>,
) -> AppResult<Json<BookResponse>> {
tracing::info!("テスト用のログ");
// ...
}エンドポイントを呼び出すと、イベントにスパンの情報が付いて出力される。
{
"timestamp": "2024-10-14T08:20:40.016863Z",
"level": "INFO",
"fields": { "message": "テスト用のログ" },
"filename": "api/src/handler/book.rs",
"line_number": 103,
"span": {
"book_id": "BookId(74fa2a39-674c-4b02-9969-2b5183b6b924)",
"user_id": "8b0977724de6464baaf0e3a6cf5d470b",
"name": "show_book"
},
"spans": [
{
"method": "GET",
"uri": "/api/v1/books/74fa2a39-674c-4b02-9969-2b5183b6b924",
"version": "HTTP/1.1",
"name": "request"
},
{
"book_id": "BookId(74fa2a39-674c-4b02-9969-2b5183b6b924)",
"user_id": "8b0977724de6464baaf0e3a6cf5d470b",
"name": "show_book"
}
]
}注意点
#[tracing::instrument]を付けただけでは何も出力されない。ログを出すには、tracing::info!などのマクロでイベントを発行する必要がある#[tracing::instrument]は、何も設定しないと関数の引数をすべてスパンに含める。含めたくない引数はskipで指定する