common_uu 1.9.4

公共工具库
Documentation
use std::{env, vec};

use crate::{dev_or_prod, string::StringExentd, IResult};
use log::LevelFilter;
use once_cell::sync::Lazy;

pub static EXE_FILE_NAME: Lazy<String> = Lazy::new(|| {
    let mut filename = std::env::current_exe()
        .unwrap_or_default()
        .as_path()
        .file_name()
        .map(|x| x.to_str().map(|x| x.to_string()))
        .unwrap_or_default()
        .unwrap_or_default();
    if filename.find(|x| x == '.').is_some() {
        let (filename2, _) = filename.split_char2(".");
        filename = filename2;
    }
    filename
});

pub fn init() -> IResult {
    use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
    use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
    use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
    use log4rs::{
        append::{
            console::{ConsoleAppender, Target},
            rolling_file::RollingFileAppender,
        },
        config::{Appender, Root},
        encode::pattern::PatternEncoder,
        filter::threshold::ThresholdFilter,
    };

    let dir = format!("logs-{}", EXE_FILE_NAME.to_string());
    println!("logs path: {}", dir);
    // let parttern = "{d(%Y-%m-%d %H:%M:%S.%f)} {l} [{t}:{L}] {m}\n";
    let parttern = "{d(%Y-%m-%d %H:%M:%S.%6f)} {l} [{f}:{L}] {m}\n";
    let file_size = 1024 * 1024 * 100;
    // let file_size = 512;

    let logfile_get = |filter: log::LevelFilter, level_name: &str| {
        let builder = RollingFileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(parttern)))
            .build(
                format!("{dir}/{level_name}.log"),
                Box::new(CompoundPolicy::new(
                    Box::new(SizeTrigger::new(file_size)),
                    Box::new(FixedWindowRoller::builder().base(1).build(format!("{dir}/{level_name}.{{}}.log").as_str(), 30).unwrap()),
                )),
            )
            .unwrap();
        Appender::builder().filter(Box::new(ThresholdFilter::new(filter))).build(level_name, Box::new(builder))
    };

    let mut appenders = vec!["debug", "info", "warn", "error"];

    // 开发环境打印日志到控制台,release编译,生产环境时不打印
    if dev_or_prod!(true, false) {
        appenders.push("console");
    }

    let config = log4rs::Config::builder()
        .appender(logfile_get(log::LevelFilter::Debug, "debug"))
        .appender(logfile_get(log::LevelFilter::Info, "info"))
        .appender(logfile_get(log::LevelFilter::Warn, "warn"))
        .appender(logfile_get(log::LevelFilter::Error, "error"))
        .appender(Appender::builder().filter(Box::new(ThresholdFilter::new(log::LevelFilter::Debug))).build(
            "console",
            Box::new(ConsoleAppender::builder().encoder(Box::new(PatternEncoder::new(parttern))).target(Target::Stdout).build()),
        ))
        .logger(log4rs::config::Logger::builder().build("ureq", LevelFilter::Info))
        .logger(log4rs::config::Logger::builder().build("rbatis", dev_or_prod!(LevelFilter::Info, LevelFilter::Warn)))
        .logger(log4rs::config::Logger::builder().build("rustls", LevelFilter::Info))
        .logger(log4rs::config::Logger::builder().build("reqwest", LevelFilter::Info))
        .logger(log4rs::config::Logger::builder().build("hyper", LevelFilter::Info))
        .logger(log4rs::config::Logger::builder().build("sled", LevelFilter::Info))
        .logger(log4rs::config::Logger::builder().build("isahc", LevelFilter::Warn))
        .logger(log4rs::config::Logger::builder().build("mqtt_async_client", LevelFilter::Warn))
        .logger(log4rs::config::Logger::builder().build("teloxide", LevelFilter::Off))
        .logger(log4rs::config::Logger::builder().build("tao::platform_impl", LevelFilter::Error))
        .build(Root::builder().appenders(appenders).build(log::LevelFilter::Debug))
        .unwrap();

    log4rs::init_config(config)?;
    Ok(())
}

/// log_level: 日志级别, 比如 "debug,info,warn,error", 根据 std::env::set_var("RUST_LOG", "debug") 的值来决定, 还可以 std::env::set_var("RUST_LOG", "debug,reqwest=warn") 来设置单个库的日志级别
/// off_libs: 不打印日志的库, 用逗号分隔, 比如 "reqwest,hyper,isahc"
pub fn init_by_off(off_libs: impl Into<String>) -> IResult {
    let name = EXE_FILE_NAME.to_string();
    init_by_off2(off_libs, name)
}

/// log_level: 日志级别, 比如 "debug,info,warn,error", 根据 std::env::set_var("RUST_LOG", "debug") 的值来决定, 还可以 std::env::set_var("RUST_LOG", "debug,reqwest=warn") 来设置单个库的日志级别
/// off_libs: 不打印日志的库, 用逗号分隔, 比如 "reqwest,hyper,isahc"
/// log_name: 日志文件名, 如果传 xxx, 则日志文件夹称为 logs-xxx, 里面有 debug, info, warn, error 四个文件
pub fn init_by_off2(off_libs: impl Into<String>, log_name: impl Into<String>) -> IResult {
    init3(off_libs, log_name, true)
}

/// log_level: 日志级别, 比如 "debug,info,warn,error", 根据 std::env::set_var("RUST_LOG", "debug") 的值来决定, 还可以 std::env::set_var("RUST_LOG", "debug,reqwest=warn") 来设置单个库的日志级别
/// off_libs: 不打印日志的库, 用逗号分隔, 比如 "reqwest,hyper,isahc"
/// log_name: 日志文件名, 如果传 xxx, 则日志文件夹称为 logs-xxx, 里面有 debug, info, warn, error 四个文件
/// foce_write_to_file: 是否强制写入文件, 如果为true, 则不管log_level的值, 都写入文件, 否则根据log_level的值来决定是否写入文件
pub fn init3(off_libs: impl Into<String>, log_name: impl Into<String>, foce_write_to_file: bool) -> IResult {
    let off_libs = off_libs.into();
    use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
    use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
    use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
    use log4rs::{
        append::{
            console::{ConsoleAppender, Target},
            rolling_file::RollingFileAppender,
        },
        config::{Appender, Root},
        encode::pattern::PatternEncoder,
        filter::threshold::ThresholdFilter,
    };

    let dir = format!("logs-{}", log_name.into());
    println!("logs path: {}", dir);
    // let parttern = "{d(%Y-%m-%d %H:%M:%S.%f)} {l} [{t}:{L}] {m}\n";
    let parttern = "{d(%Y-%m-%d %H:%M:%S.%6f)} {l} [{f}:{L}] {m}\n";
    let file_size = 1024 * 1024 * 100;
    // let file_size = 512;
    let mut appenders = vec!["console".to_string()];

    let mut logfile_get = |filter: log::LevelFilter| {
        let level_name = filter.as_str().to_lowercase();
        let builder = RollingFileAppender::builder()
            .encoder(Box::new(PatternEncoder::new(parttern)))
            .build(
                format!("{dir}/{level_name}.log"),
                Box::new(CompoundPolicy::new(
                    Box::new(SizeTrigger::new(file_size)),
                    Box::new(FixedWindowRoller::builder().base(1).build(format!("{dir}/{level_name}.{{}}.log").as_str(), 30).unwrap()),
                )),
            )
            .unwrap();
        let level_name = format!("file_{level_name}");
        appenders.push(level_name.clone());
        Appender::builder().filter(Box::new(ThresholdFilter::new(filter))).build(level_name, Box::new(builder))
    };

    // 开发环境打印日志到控制台,release编译,生产环境时只有warn,error日志才打印
    let log_level = env::var("RUST_LOG").unwrap_or_else(|_| if cfg!(debug_assertions) { "debug".to_string() } else { "warn".to_string() });

    // 考虑 RUST_LOG = "debug,xxx=warn" 的情况
    let log_level = log_level.split(",").map(|x| x.trim().to_string()).collect::<Vec<String>>();

    let console_level = log_level[0].trim().to_uppercase().parse::<log::LevelFilter>().unwrap_or(log::LevelFilter::Warn);
    if console_level == log::LevelFilter::Off {
        return Ok(());
    }
    // let console_level = dev_or_prod!(log::LevelFilter::Debug, log::LevelFilter::Warn);

    let mut config = log4rs::Config::builder().appender(Appender::builder().filter(Box::new(ThresholdFilter::new(console_level))).build(
        "console",
        Box::new(ConsoleAppender::builder().encoder(Box::new(PatternEncoder::new(parttern))).target(Target::Stdout).build()),
    ));

    // 写入文件的日志级别
    let log_levels = [log::LevelFilter::Trace, log::LevelFilter::Debug, log::LevelFilter::Info, log::LevelFilter::Warn, log::LevelFilter::Error];
    for l in log_levels {
        if foce_write_to_file || console_level >= l {
            config = config.appender(logfile_get(l));
        }
    }

    // 考虑 RUST_LOG = "xxx=warn" 的情况
    if log_level.len() > 1 {
        for i in 1..log_level.len() {
            let (name, level) = log_level[i].split_char2("=");
            let name = name.trim().to_string();
            if name.is_empty() || level.is_empty() {
                continue;
            }
            let level = level.trim().to_uppercase().parse::<log::LevelFilter>().unwrap_or(log::LevelFilter::Warn);

            // 添加日志配置
            config = config.logger(log4rs::config::Logger::builder().build(name, level));
        }
    }

    // 添加日志配置:这些模块不打印日志
    let offs = off_libs.split_char(",");
    for off in offs {
        if off.trim().is_empty() {
            continue;
        }

        config = config.logger(log4rs::config::Logger::builder().build(off, LevelFilter::Off));
    }

    /* .logger(log4rs::config::Logger::builder().build("ureq", LevelFilter::Info))
    .logger(
        log4rs::config::Logger::builder()
            .build("rbatis", dev_or_prod!(LevelFilter::Info, LevelFilter::Warn)),
    )
    .logger(log4rs::config::Logger::builder().build("rustls", LevelFilter::Info))
    .logger(log4rs::config::Logger::builder().build("reqwest", LevelFilter::Info))
    .logger(log4rs::config::Logger::builder().build("hyper", LevelFilter::Info))
    .logger(log4rs::config::Logger::builder().build("sled", LevelFilter::Info))
    .logger(log4rs::config::Logger::builder().build("isahc", LevelFilter::Warn))
    .logger(log4rs::config::Logger::builder().build("mqtt_async_client", LevelFilter::Warn))
    .logger(log4rs::config::Logger::builder().build("teloxide", LevelFilter::Off))
    .logger(log4rs::config::Logger::builder().build("tao::platform_impl", LevelFilter::Error)) */

    // 2025-10-06 13:53:19.292188200 ERROR [src\log4rs_mod.rs:240] test error log
    // 修改以上内容格式化日志的时间格式, 显示到微秒即可

    let config = config.build(Root::builder().appenders(appenders).build(log::LevelFilter::Trace))?;

    log4rs::init_config(config)?;
    Ok(())
}

#[test]
fn test() {
    init().unwrap();
    for i in 0..100 {
        debug!("test debug log");
        info!("test info log");
        warn!("test warn log");
        error!("test error log");
        std::thread::sleep(std::time::Duration::from_millis(1000));
    }
}

#[test]
fn test3() {
    unsafe { std::env::set_var("RUST_LOG", "warn,reqwest2=debug") };
    init3("reqwest,hyper,isahc".to_string(), "test".to_string(), false).unwrap();
    for i in 0..10 {
        debug!("test debug log {i}");
        info!("test info log {i}");
        warn!("test warn log {i}");
        error!("test error log {i}");
        std::thread::sleep(std::time::Duration::from_millis(200));
    }
}