Tracing usage
    (Jin Qing's Column, Jan., 2022)
    Tracing is Rust log crate: https://github.com/tokio-rs/tracing
    This example code outputs log to stdout and a log file, using a log filter config file,
which can be automatically reloaded on change.
    
      https://gitee.com/jinq0123/tracing-example
    
    Dependencies
    Add these dependencies to Cargo.toml:
          [dependencies]
anyhow = "1.0.52"
hotwatch = "0.4.6"
tracing = "0.1.29"
tracing-appender = "0.2.0"
tracing-subscriber = { version = "0.3.5", features = [ "env-filter", "json" ] }
    
    main.rs
          mod log;
use anyhow::Result;
use std::{thread, time::Duration};
use tracing::info;
fn main() -> Result<()> {
    let _guard = log::init("./log", "example.log", "config/log_filter.txt")?;
    for i in 0..999 {
        info!(i, "Hello, world!");
        thread::sleep(Duration::from_secs(1));
    }
    Ok(())
}
    
    log.rs
          //! Init log.
//!
use anyhow::{anyhow, Context as _, Result};
use hotwatch::{Event, Hotwatch};
use std::fs;
use std::path::Path;
use tracing::{debug, warn, Subscriber};
use tracing_appender::{non_blocking::WorkerGuard, rolling};
use tracing_subscriber::{fmt, layer::SubscriberExt, reload::Handle, EnvFilter};
/// Inits log.
/// Returns a WorkerGuard to ensure buffered logs are flushed,
///  and a Hotwatch to watch the log filter file.
pub fn init(
    directory: impl AsRef<Path>,
    file_name_prefix: impl AsRef<Path>,
    log_filter_file: impl AsRef<Path>,
) -> Result<(WorkerGuard, Hotwatch)> {
    let file_appender = rolling::daily(directory, file_name_prefix);
    let (non_blocking, worker_guard) = tracing_appender::non_blocking(file_appender);
    let file_layer = fmt::Layer::default()
        .with_writer(non_blocking)
        .json()
        .flatten_event(true)
        .with_ansi(false);
    let builder = tracing_subscriber::fmt()
        .pretty()
        .with_env_filter(EnvFilter::from_default_env())
        .with_filter_reloading();
    let handle = builder.reload_handle();
    let subscriber = builder.finish();
    let subscriber = subscriber.with(file_layer);
    tracing::subscriber::set_global_default(subscriber).context("set global default subscriber")?;
    reload_filter(handle.clone(), log_filter_file.as_ref());
    let log_filter_path_buf = log_filter_file.as_ref().to_path_buf();
    let mut hotwatch = Hotwatch::new().context("hotwatch failed to initialize!")?;
    hotwatch
        .watch(log_filter_file.as_ref(), move |event: Event| {
            debug!("log filter file event: {:?}", event);
            if let Event::Write(_) = event {
                reload_filter(handle.clone(), log_filter_path_buf.clone());
            }
        })
        .with_context(|| format!("failed to watch file: {:?}", log_filter_file.as_ref()))?;
    Ok((worker_guard, hotwatch))
}
fn reload_filter<S: Subscriber + 'static>(
    handle: Handle<EnvFilter, S>,
    log_filter_file: impl AsRef<Path>,
) {
    let res = try_reload_filter(handle, log_filter_file);
    match res {
        Ok(_) => debug!("reload log filter OK"),
        Err(e) => warn!("reload log filter error: {:?}", e),
    }
}
fn try_reload_filter<S: Subscriber + 'static>(
    handle: Handle<EnvFilter, S>,
    log_filter_file: impl AsRef<Path>,
) -> Result<()> {
    let contents = fs::read_to_string(log_filter_file.as_ref()).with_context(|| {
        format!(
            "something went wrong reading the file: {:?}",
            log_filter_file.as_ref()
        )
    })?;
    let contents = contents.trim();
    debug!("reload log filter: {:?}", contents);
    let new_filter = contents
        .parse::<EnvFilter>()
        .map_err(|e| anyhow!(e))
        .context("failed to parse env filter")?;
    handle.reload(new_filter).context("handle reload error")
}
    
    log_filter.txt
    log_filter.txt is the configure file for log.
The change of this file will trigger the reload.
    The log filter file must exist, otherwise it will be error:
          Error: failed to watch file: "config/log_filter.txt"
Caused by:
    系统找不到指定的路径。 (os error 3)
    
    The content of log_filter.txt is a single line of filter string.
A filter string consists of one or more comma-separated directives.
The directive syntax is similar to RUST_LOG env val of env_logger’s.
          target[span{field=value}]=level
    
    See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
    Example
    
      - 
        tracing_exampleenables logs that:- target is modules of tracing_example*
 
- 
        infowill enable logs that:
- 
        tracing_ex=infoenables logs that:- target is modules of tracing_ex*
- level is info or above
 
- 
        info,tracing_ex=debugenables logs that:- level is info or above
- or target is tracing_ex* and level is debug
 
- 
        [foo]=traceenables logs that:- within the span foo
- level is trace or above
 
- 
        [span_b{name=\"bob\"}]enables logs that:- have any target,
- are inside a span named span_b,
- which has a field named name with value bob,
- at any level.
 
Note: span filter directive will keep effective until it exits the span.