log-full 0.0.1

A simple, asynchronous log library
Documentation
//! 工具函数模块
//! 
//! 提供日志库使用的各种工具函数和辅助类型

use crate::error::{LogError, LogResult};
use std::collections::VecDeque;
use std::fs::File;
use std::io::{LineWriter, Write};
use std::str::FromStr;
use std::sync::Mutex;

/// 缓存字符串初始大小
pub const CACHE_STR_INIT_SIZE: usize = 256;

/// 异步日志类型
pub enum AsyncLogType {
    Message(Vec<u8>),
    Flush,
    QuickwitLog(crate::quickwit::QuickwitLogEntry),
}

/// ANSI 颜色跳过迭代器
pub struct SkipAnsiColorIter<'a> {
    data: &'a [u8],
    pos: usize,
    find_len: usize,
}

impl<'a> SkipAnsiColorIter<'a> {
    pub fn new(data: &'a [u8]) -> Self {
        let find_len = if data.len() > 3 {
            data.len() - 3
        } else {
            0
        };

        SkipAnsiColorIter {
            data,
            pos: 0,
            find_len,
        }
    }
}

impl<'a> Iterator for SkipAnsiColorIter<'a> {
    type Item = &'a [u8];

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        // 过滤ansi颜色
        let (mut pos, find_len, data) = (self.pos, self.find_len, self.data);
        while pos < find_len {
            unsafe {
                if *data.get_unchecked(pos) != 0x1b || *data.get_unchecked(pos + 1) != b'[' {
                    pos += 1;
                    continue;
                }

                // 找到ansi颜色前缀,返回前缀前的字符串并更新当前位置和已写入字节
                let n = if *data.get_unchecked(pos + 3) == b'm' { 4 } else { 5 };
                let p = self.pos;
                self.pos = pos + n;
                return Some(&data[p..pos]);
            }
        }

        // 写入剩余的数据
        let dl = data.len();
        if pos < dl {
            let p = self.pos;
            self.pos = dl;
            return Some(&data[p..dl]);
        }

        None
    }
}

/// 解析日志级别
pub fn parse_level(level: &str) -> LogResult<log::LevelFilter> {
    match log::LevelFilter::from_str(level) {
        Ok(num) => Ok(num),
        Err(_) => Err(format!("can't parse log level: {level}").into()),
    }
}

/// 解析文件大小
pub fn parse_size(size: &str) -> LogResult<u32> {
    match size.parse() {
        Ok(n) => Ok(n),
        Err(_) => match size[..size.len() - 1].parse() {
            Ok(n) => {
                let s = size.as_bytes();
                match s[s.len() - 1] {
                    b'b' | b'B' => Ok(n),
                    b'k' | b'K' => Ok(n * 1024),
                    b'm' | b'M' => Ok(n * 1024 * 1024),
                    b'g' | b'G' => Ok(n * 1024 * 1024 * 1024),
                    _ => Err(format!("parse size error, unit is unknown: {size}").into()),
                }
            },
            Err(e) => Err(e.into()),
        }
    }
}

/// 写入文本到文件
pub fn write_text(w: &mut LineWriter<File>, msg: &[u8]) -> std::io::Result<usize> {
    let mut written = 0;
    for chunk in SkipAnsiColorIter::new(msg) {
        w.write_all(chunk)?;
        written += chunk.len();
    }
    Ok(written)
}

/// 安全刷新写入器
#[inline]
pub fn safe_flush<W: Write>(writer: &mut W) -> LogResult<()> {
    writer.flush().map_err(LogError::from)
}

/// 打开日志文件(同步方式)
pub fn open_log_file_sync(file_path: &str) -> LogResult<(LineWriter<File>, u32)> {
    use std::fs::OpenOptions;
    use std::io::Seek;
    
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(file_path)
        .map_err(|e| LogError::file_operation(
            file_path.to_string(),
            format!("Failed to open file: {}", e)
        ))?;
    
    let size = file.seek(std::io::SeekFrom::End(0))
        .map_err(|e| LogError::file_operation(
            file_path.to_string(),
            format!("Failed to seek file: {}", e)
        ))? as u32;
    
    let writer = LineWriter::new(file);
    Ok((writer, size))
}

/// 获取日志级别对应的颜色
pub fn level_color(level: log::Level) -> &'static str {
    match level {
        log::Level::Error => "\x1b[31m",  // 红色
        log::Level::Warn => "\x1b[33m",   // 黄色
        log::Level::Info => "\x1b[32m",   // 绿色
        log::Level::Debug => "\x1b[36m",  // 青色
        log::Level::Trace => "\x1b[35m",  // 紫色
    }
}

/// 从缓存获取消息缓冲区
pub fn get_msg_from_cache() -> Vec<u8> {
    static FMT_CACHE: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new());
    
    if let Ok(mut cache) = FMT_CACHE.lock() {
        cache.pop_front().unwrap_or_else(|| Vec::with_capacity(CACHE_STR_INIT_SIZE))
    } else {
        Vec::with_capacity(CACHE_STR_INIT_SIZE)
    }
}

/// 将消息缓冲区放回缓存
pub fn put_msg_to_cache(mut value: Vec<u8>) {
    static FMT_CACHE: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new());
    
    value.clear();
    if let Ok(mut cache) = FMT_CACHE.lock() {
        if cache.len() < 16 {
            cache.push_back(value);
        }
    }
}

/// 调试检查初始化
pub fn debug_check_init() {
    #[cfg(debug_assertions)]
    {
        println!("log-full debug mode enabled");
    }
}

/// 获取版本信息
pub fn version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

/// 获取构建信息
pub fn build_info() -> (&'static str, &'static str) {
    (env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_NAME"))
}