2022/01/15

[Rust]loggerを自作してpanic時のエラー検知を行う

rusterrbit

概要

Rustでloggerを自作しました。

隙間時間を使ってコツコツ作りました。(思ったより時間がかかってしまいました。)

以下のような感じの機能を持たせています

  • 複数の出力先を持たせる(2022/01/15では以下対応)
    • 標準エラーに出力
    • ファイルに出力
    • TCP経由で出力(logstashなどを想定)
    • HTTP(S)経由で出力(airbrake/errbitを想定)
  • 拡張性
    • このライブラリが提供するtraitを実装することで、ライブラリ利用先のプロジェクトで自前のloggerを拡張可能
  • 非同期/同期出力
    • httpやtcpでの出力機能があるため、ログ出力を非同期スレッドにて出力できるようにしています。(同じスレッドでの同期出力も可能)
  • ログ出力時にbacktraceを出力可能
  • panic 時にbacktraceと共に、panic情報を出力(airbrake/errbitに通知)できるようにしました

使い方

Cargo.toml

[dependencies]
logger = { version = "0.1.0", git = "https://github.com/kumanote/logger-rs", branch = "main", features = ["airbrake"] }
use logger::default::DefaultLoggerBuilder;
use logger::prelude::*;
use logger::setup_panic_logger;
use logger::Level;

use actix_web::{web, App, HttpServer};

fn double_number(number_str: &str) -> i32 {
    number_str
        .parse::<i32>()
        .map(|n| 2 * n)
        .expect("number_str must be valid number string")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let mut builder = DefaultLoggerBuilder::new();
    builder.is_async(true);
    builder.level(Level::Debug);
    builder.airbrake_host("https://api.airbrake.io".to_owned());
    builder.airbrake_project_id("<YOUR PROJECT ID>".to_owned());
    builder.airbrake_project_key("<YOUR API KEY>".to_owned());
    builder.airbrake_environment("production".to_owned());
    let _logger = builder.build();
    setup_panic_logger();

    HttpServer::new(move || App::new().configure(routes))
        .bind("0.0.0.0:8000")?
        .run()
        .await
}

pub fn routes(app: &mut web::ServiceConfig) {
    app.service(web::resource("/").route(web::get().to(index)));
}

pub async fn index(_req: web::HttpRequest) -> &'static str {
    debug!("this is a simple debug log");
    trace!("this is {}", "test");
    let value1 = 5;
    info!(key1 = value1);
    let _number = double_number("NOT A NUMBER");
    "OK"
}
  • DefaultLoggerBuilderを使って、ロガーの設定を行います。
    • コンソール出力と(エラー時には)airbrake(errbit)に通知できるようにしています。
    • 非同期でのログ出力を行うようにしています。
  • setup_panic_loggerでpanic時にクラッシュログをairbrake(errbit)に通知できるようにしました。

panic handler

panic時のログ出力のtips

setup_panic_loggerの中身です。
panic::set_hookを使って、panic時にログ出力を行うように設定しています。

use crate::flush;
use crate::prelude::crash;
use std::panic::{self, PanicInfo};

pub fn setup_panic_logger() {
    panic::set_hook(Box::new(move |pi: &PanicInfo<'_>| {
        handle_panic(pi);
    }));
}

fn handle_panic(panic_info: &PanicInfo<'_>) {
    let details = format!("{}", panic_info);
    crash!("{}", details);
    flush();
}

今後は弊社の複数のrustプロジェクトで使っていこうと思います。

以上になります。