Skip to main content

pd_rs_common/
logger.rs

1//! 日志记录模块
2
3use std::io;
4use std::fmt::Write;
5use tracing::{Event, Subscriber};
6use tracing::field::{Field, Visit};
7use tracing_appender::non_blocking::WorkerGuard;
8use tracing_subscriber::layer::Context;
9use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter, Layer, Registry};
10
11pub struct StringVisitor<'a> {
12    pub string: &'a mut String,
13}
14
15impl<'a> Visit for StringVisitor<'a> {
16    fn record_debug(&mut self, _field: &Field, value: &dyn std::fmt::Debug) {
17        // write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
18        let _ = write!(self.string, "{:?}", value);
19    }
20}
21
22pub struct CustomLayer {
23    pub callback: fn(&Event<'_>),
24}
25
26impl CustomLayer {
27    pub fn new(f: fn(_event: &Event<'_>)) -> Self {
28        Self { callback: f }
29    }
30}
31impl Default for CustomLayer {
32    fn default() -> Self {
33        CustomLayer::new(|_| {})
34    }
35}
36
37impl<S> Layer<S> for CustomLayer
38where
39    S: Subscriber,
40{
41    fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {
42        (self.callback)(_event);
43    }
44}
45
46/// 初始化统一的 tracing 订阅器,
47/// 返回 `WorkerGuard` 确保日志写入器生命周期正确
48pub fn init_tracing(
49    max_log_files: Option<u32>,
50    custom_hook: Option<fn(&Event<'_>)>,
51) -> (WorkerGuard, WorkerGuard) {
52    // 默认日志级别为 "info",可通过 RUST_LOG 覆盖
53    let env_filter = EnvFilter::try_from_default_env()
54        .or_else(|_| EnvFilter::try_new("info"))
55        .unwrap();
56
57    // 输出到控制台(非阻塞)
58    let (console_writer, console_guard) = tracing_appender::non_blocking(io::stdout());
59
60    let file_appender = tracing_appender::rolling::RollingFileAppender::builder()
61        .rotation(tracing_appender::rolling::Rotation::DAILY) // rotate log files once every day
62        .filename_prefix("app") // log file names will be prefixed with `myapp.`
63        .filename_suffix("log") // log file names will be suffixed with `.log`
64        .max_log_files(max_log_files.unwrap_or(2) as usize) // max log files
65        .build("logs") // try to build an appender that stores log files in `/var/log`
66        .expect("initializing rolling file appender failed");
67    let (file_writer, file_guard) = tracing_appender::non_blocking(file_appender);
68
69    let console_layer = fmt::layer()
70        .with_timer(fmt::time::ChronoLocal::rfc_3339())
71        .with_target(false)
72        .with_writer(console_writer)
73        .with_ansi(atty::is(atty::Stream::Stdout)) // 自动检测TTY启用颜色
74        .with_file(true) // 显示文件名
75        .with_line_number(true) // 显示行号
76        .with_thread_names(false)
77        .compact();
78
79    let file_layer = fmt::layer()
80        .with_timer(fmt::time::ChronoLocal::rfc_3339())
81        .with_target(false)
82        .with_writer(file_writer)
83        .with_ansi(false)
84        .with_file(true)
85        .with_line_number(true)
86        .with_thread_names(false)
87        .compact();
88
89    let custom_layer = if let Some(fn_hook) = custom_hook {
90        CustomLayer::new(fn_hook)
91    } else {
92        CustomLayer::default()
93    };
94
95    // 组合所有 Layers
96    let subscriber = Registry::default()
97        .with(env_filter)
98        .with(console_layer)
99        .with(file_layer)
100        .with(custom_layer);
101
102    tracing::subscriber::set_global_default(subscriber)
103        .expect("Failed to set global tracing subscriber");
104
105
106    // 返回 guard 避免日志写入器被提前释放
107    (console_guard, file_guard)
108}