Rustのloggerについて（fernクレート）

## 概要
- Rustの標準的なロギングの仕組みはlogクレートが中心
    - logクレートはcrates.ioにおいて歴代ダウンロード数が10位のメジャーなクレート
- logクレートはロギングに必要なAPIを定義しつつも、ロガーの実装は他のクレートに委ねるファサードパターン
    - アプリケーション・ライブラリ向けには5種類のログレベルに応じたロギング用のマクロ
    - ロガーを実装するクレート向けには実装すべきtrait Log
- logクレートにはNopLoggerという何もしないロガーが定義
    - 他のクレートから log::set_logger() が呼ばれるまでは、このNopLoggerを設定
        - メッセージに対して何もしません
    - ロガークレートの init という関数から log::set_logger() が呼び出され、ロガーの切り替えができるようになっている
        - ロガーの切り替えはプログラム中、一度しかできない
- 非同期処理のロギングはtracingクレートで実装

## ロガーを実装しているクレート

| クレート | メリット | デメリット |
| --- | --- | --- |
| env_logger | 環境変数で出力するログを変更可能 | 出力先が標準エラーに固定 |
| simplelog | 出力先の制御が可能 | ログのフォーマットは変更不可 |
| fern | 出力先やフォーマット・ログのレベルを設定可能 | 設定が複雑 |

## toml
```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を記録 |

```shell:install
$ cargo add log
$ cargo add fern
$ cargo add chrono
```

```shell:ディレクトリ構造
.
├── Cargo.toml
└── src
    ├── lib.rs
    └── main.rs
```
```rust: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!("エラー");
}

```

```shell:実行結果
$ 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クレート](https://crates.io/crates/log)
- [tracing](https://crates.io/crates/tracing)
- [Rustのロギングについて解説とデモ](https://www.forcia.com/blog/001605.html)
- [ファサードパターン](https://www.techscore.com/tech/DesignPattern/Facade#:~:text=Facade%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%AF%E3%80%81%E6%97%A2%E5%AD%98%E3%81%AE,%E3%81%AE%E6%AD%A3%E9%9D%A2%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E6%84%8F%E5%91%B3%E3%81%A7%E3%81%99%E3%80%82)
    - 共通のインターフェイスを提供して、どの実装を使うかによらず、利用者から簡単に利用できるようにするデザインパターン