How can I write logs to different files with `tracing`?

I have the following code:

use tracing::Level;
use tracing_subscriber::{
    layer::{Layer, SubscriberExt},
    filter,
    util::SubscriberInitExt,
    fmt::{ self, format::Writer, time::FormatTime }
};
use chrono::Local;

struct LocalTimer;

impl FormatTime for LocalTimer {
    fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
        write!(w, "{}", Local::now().format("%Y-%m-%d %H:%M:%S"))
    }
}

#[derive(Debug, Clone, Copy)]
pub enum Ticker {
    A,
    B,
    C,
    D,
}



fn main() {
    let ticker_vec = vec![Ticker::A, Ticker::B, Ticker::C, Ticker::D];
    let mut guard_vec = Vec::with_capacity(ticker_vec.len());
    let layer_vec = ticker_vec
        .into_iter()
        .map(|ticker| {
            let ticker_string = format!("{:?}", ticker);
            let (non_blocking, guard) =  tracing_appender::non_blocking(tracing_appender::rolling::daily(
                "./logs",
                ticker_string.clone(),
            ));
            let filter_ticker = filter::filter_fn(move |metadata| {
                metadata.target() == &ticker_string
            });
            let layer_ticker = fmt::layer()
                .with_writer(non_blocking)
                .with_filter(filter_ticker);
            guard_vec.push(guard);
            layer_ticker.boxed()
        })
        .collect::<Vec<_>>();
    let json_layer = fmt::layer()
        .with_ansi(false) 
        .with_file(true)
        .with_line_number(true) 
        .with_timer(LocalTimer)
        .json()
        .flatten_event(true);

    tracing_subscriber::registry()
        .with(layer_vec)
        .with(json_layer)
        .init();

    let ticker_vec_runtime = vec![Ticker::A, Ticker::C];
    for ticker in ticker_vec_runtime.iter() {
        tracing::info!(target: ticker, "run time ticker vec");
    }
    // let span = tracing::span!(Level::INFO, "aaa", value = ?ticker1);
    // let _guard = span.enter();
    // tracing::info!(target = ?ticker1, "Aaa1");
    // tracing::info!(target = ?ticker2, "baa1");
    // tracing::info!(target: "al", "Aaa");
    // tracing::info!(target: "eb", "baa");
}

It has the following error:

attempt to use a non-constant value in a constant
non-constant valuerustcClick for full compiler diagnostic
macros.rs(855, 9): consider using `let` instead of `static`: `let __CALLSITE

The var ticker_vec_running cannot be known at compile time. So how can I write the logs to different files in runtime?

Is there anyone could help me?

Never having worked with tracing, as far as I understand the docs, target: does not refer to a log file, but to a metadata field of the tracing event. You can configure tracing to log to different files by letting them subscribe to the tracing facility. However, even after reading the docs of tracing, tracing-subscriber and tracing-log I was unable to find any example on how to let tracing log to a file.

After some more research, I got tracing to log to two separate files:

use std::fs::File;
use tracing::{info, span, Level};
use tracing_subscriber::{fmt::layer, layer::SubscriberExt, util::SubscriberInitExt, Registry};

fn main() {
    let log1 = File::create("tracing1.log").unwrap();
    let log2 = File::create("tracing2.log").unwrap();
    let file_layer1 = layer().with_writer(log1);
    let file_layer2 = layer().with_writer(log2);
    Registry::default()
        .with(file_layer1)
        .with(file_layer2)
        .init();
    let span = span!(Level::TRACE, "my_span");
    let _enter = span.enter();
    info!("test event");
}
3 Likes

Yes, logging into sepearate files (known at compiling time) is fine. But how can I logging into seperate files (only can be known at running time)?

Nothing of the above code requires static context. You should be able to dynamically call Registry::with() on the instance from e.g. user-defined input. Using Layer::with_filter() you're also able to filter what events go into which log.