Skip to main content

xyz_rust/
logx.rs

1// logx 是库自身的诊断出口:写给 stderr 的分级日志(xyz[level]: 前缀)。
2// 默认级别 Info;根派发器读取 --xyz.log-level(代码侧:logx::set_level)。
3//
4// 库只在这里写诊断——命令结果与用法错误走各自输出流,不受日志级别影响。
5
6use std::sync::atomic::{AtomicI32, Ordering};
7
8/// Level 是冗余级别。数值越大越安静。
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
10#[repr(i32)]
11#[derive(Default)]
12pub enum Level {
13    /// 排在 0 位:Config.log_level 的零值,语义为「保持默认」。
14    #[default]
15    Unset = 0,
16    /// 派发/协商追踪(调试 MCP 时最有用)。
17    Debug = 1,
18    /// 默认级别:启动通知、所选的模式。
19    Info = 2,
20    /// 只留警告(被禁用的通道、未受保护的传输)。
21    Warn = 3,
22    /// 只留错误诊断。
23    Error = 4,
24}
25
26impl Level {
27    pub fn as_str(&self) -> &'static str {
28        match self {
29            Level::Unset => "info",
30            Level::Debug => "debug",
31            Level::Info => "info",
32            Level::Warn => "warn",
33            Level::Error => "error",
34        }
35    }
36}
37
38static CURRENT: AtomicI32 = AtomicI32::new(Level::Info as i32);
39
40/// 设置进程级冗余级别;Unset 或越界值保持现状。
41pub fn set_level(l: Level) {
42    match l {
43        Level::Debug | Level::Info | Level::Warn | Level::Error => {
44            CURRENT.store(l as i32, Ordering::Relaxed);
45        }
46        Level::Unset => {}
47    }
48}
49
50/// 报告 l 级别的消息是否会被打印。
51pub fn enabled(l: Level) -> bool {
52    (l as i32) >= CURRENT.load(Ordering::Relaxed)
53}
54
55/// 把 flag 值(debug|info|warn|error)映射成 Level。
56pub fn parse_level(s: &str) -> Result<Level, crate::errors::Error> {
57    match s.trim().to_ascii_lowercase().as_str() {
58        "debug" => Ok(Level::Debug),
59        "info" => Ok(Level::Info),
60        "warn" | "warning" => Ok(Level::Warn),
61        "error" => Ok(Level::Error),
62        _ => Err(crate::errors::Error::new(
63            crate::errors::Kind::Internal,
64            format!("unknown log level {s:?} (want debug|info|warn|error)"),
65        )),
66    }
67}
68
69fn emit(l: Level, msg: std::fmt::Arguments<'_>) {
70    if !enabled(l) {
71        return;
72    }
73    eprintln!("xyz[{}]: {}", l.as_str(), msg);
74}
75
76pub fn debugf(args: std::fmt::Arguments<'_>) {
77    emit(Level::Debug, args);
78}
79
80pub fn infof(args: std::fmt::Arguments<'_>) {
81    emit(Level::Info, args);
82}
83
84pub fn warnf(args: std::fmt::Arguments<'_>) {
85    emit(Level::Warn, args);
86}
87
88pub fn errorf(args: std::fmt::Arguments<'_>) {
89    emit(Level::Error, args);
90}
91
92#[macro_export]
93macro_rules! log_debug {
94    ($($arg:tt)*) => { $crate::logx::debugf(format_args!($($arg)*)) };
95}
96#[macro_export]
97macro_rules! log_info {
98    ($($arg:tt)*) => { $crate::logx::infof(format_args!($($arg)*)) };
99}
100#[macro_export]
101macro_rules! log_warn {
102    ($($arg:tt)*) => { $crate::logx::warnf(format_args!($($arg)*)) };
103}
104#[macro_export]
105macro_rules! log_error {
106    ($($arg:tt)*) => { $crate::logx::errorf(format_args!($($arg)*)) };
107}
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn parse_level_forms() {
114        assert_eq!(parse_level("debug").unwrap(), Level::Debug);
115        assert_eq!(parse_level("INFO").unwrap(), Level::Info);
116        assert_eq!(parse_level("warning").unwrap(), Level::Warn);
117        assert_eq!(parse_level("error").unwrap(), Level::Error);
118        assert!(parse_level("verbose").is_err());
119        assert!(parse_level("").is_err());
120    }
121
122    #[test]
123    fn set_level_and_enabled() {
124        set_level(Level::Error);
125        assert!(!enabled(Level::Info));
126        assert!(enabled(Level::Error));
127        set_level(Level::Debug);
128        assert!(enabled(Level::Info));
129    }
130}