Skip to main content

log_full/
utils.rs

1//! 工具函数模块
2//! 
3//! 提供日志库使用的各种工具函数和辅助类型
4
5use crate::error::{LogError, LogResult};
6use std::collections::VecDeque;
7use std::fs::File;
8use std::io::{LineWriter, Write};
9use std::str::FromStr;
10use std::sync::Mutex;
11
12/// 缓存字符串初始大小
13pub const CACHE_STR_INIT_SIZE: usize = 256;
14
15/// 异步日志类型
16pub enum AsyncLogType {
17    Message(Vec<u8>),
18    Flush,
19    QuickwitLog(crate::quickwit::QuickwitLogEntry),
20}
21
22/// ANSI 颜色跳过迭代器
23pub struct SkipAnsiColorIter<'a> {
24    data: &'a [u8],
25    pos: usize,
26    find_len: usize,
27}
28
29impl<'a> SkipAnsiColorIter<'a> {
30    pub fn new(data: &'a [u8]) -> Self {
31        let find_len = if data.len() > 3 {
32            data.len() - 3
33        } else {
34            0
35        };
36
37        SkipAnsiColorIter {
38            data,
39            pos: 0,
40            find_len,
41        }
42    }
43}
44
45impl<'a> Iterator for SkipAnsiColorIter<'a> {
46    type Item = &'a [u8];
47
48    #[inline]
49    fn next(&mut self) -> Option<Self::Item> {
50        // 过滤ansi颜色
51        let (mut pos, find_len, data) = (self.pos, self.find_len, self.data);
52        while pos < find_len {
53            unsafe {
54                if *data.get_unchecked(pos) != 0x1b || *data.get_unchecked(pos + 1) != b'[' {
55                    pos += 1;
56                    continue;
57                }
58
59                // 找到ansi颜色前缀,返回前缀前的字符串并更新当前位置和已写入字节
60                let n = if *data.get_unchecked(pos + 3) == b'm' { 4 } else { 5 };
61                let p = self.pos;
62                self.pos = pos + n;
63                return Some(&data[p..pos]);
64            }
65        }
66
67        // 写入剩余的数据
68        let dl = data.len();
69        if pos < dl {
70            let p = self.pos;
71            self.pos = dl;
72            return Some(&data[p..dl]);
73        }
74
75        None
76    }
77}
78
79/// 解析日志级别
80pub fn parse_level(level: &str) -> LogResult<log::LevelFilter> {
81    match log::LevelFilter::from_str(level) {
82        Ok(num) => Ok(num),
83        Err(_) => Err(format!("can't parse log level: {level}").into()),
84    }
85}
86
87/// 解析文件大小
88pub fn parse_size(size: &str) -> LogResult<u32> {
89    match size.parse() {
90        Ok(n) => Ok(n),
91        Err(_) => match size[..size.len() - 1].parse() {
92            Ok(n) => {
93                let s = size.as_bytes();
94                match s[s.len() - 1] {
95                    b'b' | b'B' => Ok(n),
96                    b'k' | b'K' => Ok(n * 1024),
97                    b'm' | b'M' => Ok(n * 1024 * 1024),
98                    b'g' | b'G' => Ok(n * 1024 * 1024 * 1024),
99                    _ => Err(format!("parse size error, unit is unknown: {size}").into()),
100                }
101            },
102            Err(e) => Err(e.into()),
103        }
104    }
105}
106
107/// 写入文本到文件
108pub fn write_text(w: &mut LineWriter<File>, msg: &[u8]) -> std::io::Result<usize> {
109    let mut written = 0;
110    for chunk in SkipAnsiColorIter::new(msg) {
111        w.write_all(chunk)?;
112        written += chunk.len();
113    }
114    Ok(written)
115}
116
117/// 安全刷新写入器
118#[inline]
119pub fn safe_flush<W: Write>(writer: &mut W) -> LogResult<()> {
120    writer.flush().map_err(LogError::from)
121}
122
123/// 打开日志文件(同步方式)
124pub fn open_log_file_sync(file_path: &str) -> LogResult<(LineWriter<File>, u32)> {
125    use std::fs::OpenOptions;
126    use std::io::Seek;
127    
128    let mut file = OpenOptions::new()
129        .create(true)
130        .append(true)
131        .open(file_path)
132        .map_err(|e| LogError::file_operation(
133            file_path.to_string(),
134            format!("Failed to open file: {}", e)
135        ))?;
136    
137    let size = file.seek(std::io::SeekFrom::End(0))
138        .map_err(|e| LogError::file_operation(
139            file_path.to_string(),
140            format!("Failed to seek file: {}", e)
141        ))? as u32;
142    
143    let writer = LineWriter::new(file);
144    Ok((writer, size))
145}
146
147/// 获取日志级别对应的颜色
148pub fn level_color(level: log::Level) -> &'static str {
149    match level {
150        log::Level::Error => "\x1b[31m",  // 红色
151        log::Level::Warn => "\x1b[33m",   // 黄色
152        log::Level::Info => "\x1b[32m",   // 绿色
153        log::Level::Debug => "\x1b[36m",  // 青色
154        log::Level::Trace => "\x1b[35m",  // 紫色
155    }
156}
157
158/// 从缓存获取消息缓冲区
159pub fn get_msg_from_cache() -> Vec<u8> {
160    static FMT_CACHE: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new());
161    
162    if let Ok(mut cache) = FMT_CACHE.lock() {
163        cache.pop_front().unwrap_or_else(|| Vec::with_capacity(CACHE_STR_INIT_SIZE))
164    } else {
165        Vec::with_capacity(CACHE_STR_INIT_SIZE)
166    }
167}
168
169/// 将消息缓冲区放回缓存
170pub fn put_msg_to_cache(mut value: Vec<u8>) {
171    static FMT_CACHE: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new());
172    
173    value.clear();
174    if let Ok(mut cache) = FMT_CACHE.lock() {
175        if cache.len() < 16 {
176            cache.push_back(value);
177        }
178    }
179}
180
181/// 调试检查初始化
182pub fn debug_check_init() {
183    #[cfg(debug_assertions)]
184    {
185        println!("log-full debug mode enabled");
186    }
187}
188
189/// 获取版本信息
190pub fn version() -> &'static str {
191    env!("CARGO_PKG_VERSION")
192}
193
194/// 获取构建信息
195pub fn build_info() -> (&'static str, &'static str) {
196    (env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_NAME"))
197}