tqsdk_rs/
logger.rs

1//! 日志系统
2//!
3//! 使用 tracing 实现日志系统,支持:
4//! - 多级别日志(debug, info, warn, error)
5//! - 日志过滤(只显示本库日志)
6//! - WebSocket 消息详细日志
7
8use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer};
9
10/// 日志级别
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum LogLevel {
13    /// Trace 级别
14    Trace,
15    /// Debug 级别
16    Debug,
17    /// Info 级别
18    Info,
19    /// Warn 级别
20    Warn,
21    /// Error 级别
22    Error,
23}
24
25impl LogLevel {
26    /// 转换为字符串
27    pub fn as_str(&self) -> &'static str {
28        match self {
29            LogLevel::Debug => "debug",
30            LogLevel::Info => "info",
31            LogLevel::Warn => "warn",
32            LogLevel::Error => "error",
33            LogLevel::Trace => "trace",
34        }
35    }
36}
37
38impl From<&str> for LogLevel {
39    fn from(s: &str) -> Self {
40        match s.to_lowercase().as_str() {
41            "trace" => LogLevel::Trace,
42            "debug" => LogLevel::Debug,
43            "info" => LogLevel::Info,
44            "warn" => LogLevel::Warn,
45            "error" => LogLevel::Error,
46            _ => LogLevel::Info,
47        }
48    }
49}
50
51/// 创建 tqsdk-rs 的日志 Layer
52///
53/// 返回一个配置好的 Layer,可以与业务层的其他 Layer 组合使用
54///
55/// # 参数
56/// - `level`: 日志级别 ("trace", "debug", "info", "warn", "error")
57/// - `filter_crate_only`: 是否只显示本库的日志
58///
59/// # 返回
60/// 返回一个 `impl Layer<S>` 可以与其他 Layer 组合
61///
62/// # 示例
63/// ```no_run
64/// use tqsdk_rs::create_logger_layer;
65/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
66///
67/// // 创建 tqsdk-rs 的 Layer
68/// let tqsdk_layer = create_logger_layer("debug", false);
69///
70/// // 与业务层的其他 Layer 组合
71/// tracing_subscriber::registry()
72///     .with(tqsdk_layer)
73///     // .with(your_custom_layer)
74///     .init();
75/// ```
76pub fn create_logger_layer<S>(
77    level: &str,
78    filter_crate_only: bool,
79) -> impl Layer<S> + Send + Sync + 'static
80where
81    S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
82{
83    let log_level = LogLevel::from(level);
84
85    // 构建过滤器
86    let filter = if filter_crate_only {
87        // 只显示本库的日志
88        EnvFilter::new(format!("tqsdk_rs={}", log_level.as_str()))
89    } else {
90        // 显示所有日志
91        EnvFilter::new(log_level.as_str())
92    };
93
94    // 配置格式化输出(使用本地时区)
95    fmt::layer()
96        .with_target(true)
97        .with_thread_ids(false)
98        .with_thread_names(false)
99        .with_line_number(true)
100        .with_file(true)
101        .with_ansi(true)
102        .with_timer(fmt::time::OffsetTime::local_rfc_3339().expect("无法获取本地时区"))
103        .compact()
104        .with_filter(filter)
105}
106
107/// 初始化日志系统(便捷方法)
108///
109/// 这是一个便捷方法,内部调用 `create_logger_layer` 并初始化全局订阅者
110///
111/// # 参数
112/// - `level`: 日志级别 ("trace", "debug", "info", "warn", "error")
113/// - `filter_crate_only`: 是否只显示本库的日志
114///
115/// # 示例
116/// ```no_run
117/// use tqsdk_rs::init_logger;
118///
119/// // 显示所有 debug 级别日志
120/// init_logger("debug", false);
121///
122/// // 只显示本库的 info 级别日志
123/// init_logger("info", true);
124/// ```
125pub fn init_logger(level: &str, filter_crate_only: bool) {
126    let layer = create_logger_layer(level, filter_crate_only);
127
128    // 初始化全局订阅者
129    // 使用 try_init 避免重复初始化时 panic
130    let _ = tracing_subscriber::registry()
131        .with(layer)
132        .try_init();
133}
134
135/// 初始化日志系统(带默认值)
136///
137/// 默认为 info 级别,只显示本库日志
138pub fn init_default_logger() {
139    init_logger("info", true);
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_log_level_from_str() {
148        assert_eq!(LogLevel::from("Trace"), LogLevel::Trace);
149        assert_eq!(LogLevel::from("debug"), LogLevel::Debug);
150        assert_eq!(LogLevel::from("DEBUG"), LogLevel::Debug);
151        assert_eq!(LogLevel::from("info"), LogLevel::Info);
152        assert_eq!(LogLevel::from("warn"), LogLevel::Warn);
153        assert_eq!(LogLevel::from("error"), LogLevel::Error);
154        assert_eq!(LogLevel::from("unknown"), LogLevel::Info); // 默认
155    }
156
157    #[test]
158    fn test_log_level_as_str() {
159        assert_eq!(LogLevel::Debug.as_str(), "debug");
160        assert_eq!(LogLevel::Info.as_str(), "info");
161        assert_eq!(LogLevel::Warn.as_str(), "warn");
162        assert_eq!(LogLevel::Error.as_str(), "error");
163        assert_eq!(LogLevel::Trace.as_str(), "trace");
164    }
165}