Why not log to file?

There is a problem, I need some help!! Why message not log to file?

use std::io;
use std::path::PathBuf;
use time::format_description::FormatItem;
use time::{format_description, UtcOffset};
use tracing::{error, info, instrument, trace};
use tracing_appender::non_blocking::{NonBlocking, WorkerGuard};
use tracing_subscriber::fmt::writer::MakeWriterExt;
use tracing_subscriber::{
    fmt::{time::OffsetTime, Layer},
    layer::SubscriberExt,
    EnvFilter,
};

const TIME_FORMAT: &str =
    "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]";

fn get_timer() -> OffsetTime<Vec<FormatItem<'static>>> {
    OffsetTime::new(
        UtcOffset::current_local_offset().unwrap(),
        format_description::parse(TIME_FORMAT).unwrap(),
    )
}

fn get_day_non_blocking_with_prefix(
    path: PathBuf,
    prefix: &str,
) -> (NonBlocking, WorkerGuard) {
    let appender = tracing_appender::rolling::daily(path, prefix);
    tracing_appender::non_blocking(appender)
}

fn init_logs(path: PathBuf) {
    let (inbk, _) = get_day_non_blocking_with_prefix(path.clone(), "info.log");
    let (enbk, _) = get_day_non_blocking_with_prefix(path, "error.log");

    let subscriber = tracing_subscriber::registry()
        .with(EnvFilter::from_default_env().add_directive(tracing::Level::TRACE.into()))
        .with(Layer::new().with_writer(io::stdout))
        .with(
            Layer::new()
                .pretty()
                .with_timer(get_timer())
                .with_ansi(false)
                .with_writer(enbk.with_max_level(tracing::Level::ERROR)),
        )
        .with(
            Layer::new()
                .pretty()
                .with_timer(get_timer())
                .with_ansi(false)
                .with_writer(inbk.with_max_level(tracing::Level::INFO)),
        );

    tracing::subscriber::set_global_default(subscriber)
        .expect("Unable to set a global subscriber");
}

fn main() {
    init_logs(std::env::current_dir().unwrap());
    test(12);
}

#[instrument(skip(num))]
fn test(num: i32) {
    trace!("trace");
    info!("{}", num);
    error!("hello");
}

From tracing_appender::non_blocking - Rust

A non-blocking, off-thread writer.

This spawns a dedicated worker thread which is responsible for writing log lines to the provided writer. When a line is written using the returned NonBlocking struct’s make_writer method, it will be enqueued to be written by the worker thread.

[..]

Note that the WorkerGuard returned by non_blocking must be assigned to a binding that is not _, as _ will result in the WorkerGuard being dropped immediately. Unintentional drops of WorkerGuard remove the guarantee that logs will be flushed during a program’s termination, in a panic or otherwise.

See WorkerGuard for examples of using the guard.

and from that link

WorkerGuard should be assigned in the main function or whatever the entrypoint of the program is. This will ensure that the guard will be dropped during an unwinding or when main exits successfully.

Your program is exiting after the logs are queued to be written but before the writes are actually done because you're not storing the guard:

let (inbk, _) = get_day_non_blocking_with_prefix(path.clone(), "info.log");

If you change your init_logs function so that it returns the guards, and you store the guards in main, the program works as expected:

fn init_logs(path: PathBuf) -> (WorkerGuard, WorkerGuard) {
    let (inbk, info_guard) = get_day_non_blocking_with_prefix(path.clone(), "info.log");
    let (enbk, error_guard) = get_day_non_blocking_with_prefix(path, "error.log");

    // ....

    (info_guard, error_guard)
}

fn main() {
    let _guards = init_logs(std::env::current_dir().unwrap());
    test(12);
}
4 Likes

thx a lot! problem solved.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.