概要
- Rustの標準的なロギングの仕組みはlogクレートが中心
- logクレートはcrates.ioにおいて歴代ダウンロード数が10位のメジャーなクレート
- logクレートはロギングに必要なAPIを定義しつつも、ロガーの実装は他のクレートに委ねるファサードパターン
- アプリケーション・ライブラリ向けには5種類のログレベルに応じたロギング用のマクロ
- ロガーを実装するクレート向けには実装すべきtrait Log
- logクレートにはNopLoggerという何もしないロガーが定義
- 他のクレートから log::set_logger() が呼ばれるまでは、このNopLoggerを設定
- メッセージに対して何もしません
- ロガークレートの init という関数から log::set_logger() が呼び出され、ロガーの切り替えができるようになっている
- ロガーの切り替えはプログラム中、一度しかできない
- 他のクレートから log::set_logger() が呼ばれるまでは、このNopLoggerを設定
- 非同期処理のロギングはtracingクレートで実装
ロガーを実装しているクレート
クレート | メリット | デメリット |
---|---|---|
env_logger | 環境変数で出力するログを変更可能 | 出力先が標準エラーに固定 |
simplelog | 出力先の制御が可能 | ログのフォーマットは変更不可 |
fern | 出力先やフォーマット・ログのレベルを設定可能 | 設定が複雑 |
toml
[package]
name = "application"
version = "0.1.0"
edition = "2021"
[dependencies]
fern = "0.6.1"
log = "0.4.17"
chrono = "0.4.23"
fern 実装例
logファイル | 備考 |
---|---|
application.log | INFO 以上を記録 |
emmergency.log | ERRORを記録 |
install
$ cargo add log
$ cargo add fern
$ cargo add chrono
ディレクトリ構造
.
├── Cargo.toml
└── src
├── lib.rs
└── main.rs
main.rs
fn init_logger() {
let base_config = fern::Dispatch::new();
let console_config = fern::Dispatch::new()
.level(log::LevelFilter::Trace)
.format(|out, message, record| {
out.finish(format_args! {
"[{}] {}:{} {} {}",
record.level(),
record.file().unwrap(),
record.line().unwrap(),
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
message
})
})
.chain(std::io::stdout());
let application_config = fern::Dispatch::new()
.level(log::LevelFilter::Info)
.format(|out, message, record| {
out.finish(format_args! {
"[{}] {}:{} {} {}",
record.level(),
record.file().unwrap(),
record.line().unwrap(),
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
message
})
})
.chain(fern::log_file("application.log").unwrap());
let emergency_config = fern::Dispatch::new()
.level(log::LevelFilter::Error)
.format(|out, message, record| {
out.finish(format_args! {
"[{}] {}:{} {} {}",
record.level(),
record.file().unwrap(),
record.line().unwrap(),
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
message
})
})
.chain(fern::log_file("emergency.log").unwrap());
base_config
.chain(console_config)
.chain(application_config)
.chain(emergency_config)
.apply()
.unwrap();
}
fn main() {
init_logger();
log::trace!("トレース");
log::debug!("デバッグ");
log::info!("情報");
log::warn!("警告");
log::error!("エラー");
}
実行結果
$ cargo run
[TRACE] src/main.rs:57 2022-12-12 16:34:26 トレース
[DEBUG] src/main.rs:58 2022-12-12 16:34:26 デバッグ
[INFO] src/main.rs:59 2022-12-12 16:34:26 情報
[WARN] src/main.rs:60 2022-12-12 16:34:26 警告
[ERROR] src/main.rs:61 2022-12-12 16:34:26 エラー
application.log
[INFO] src/main.rs:59 2022-12-12 16:34:26 情報
[WARN] src/main.rs:60 2022-12-12 16:34:26 警告
[ERROR] src/main.rs:61 2022-12-12 16:34:26 エラー
emergency.log
[ERROR] src/main.rs:61 2022-12-12 16:34:26 エラー
参考
- lgoクレート
- tracing
- Rustのロギングについて解説とデモ
-
ファサードパターン
- 共通のインターフェイスを提供して、どの実装を使うかによらず、利用者から簡単に利用できるようにするデザインパターン