spring boot迁移计划 第Ⅰ章 --chapter 1. rust hyper 结合rust nacos-client开发nacos网关 part ① tracing 日志
2025-06-13更新,找到一个crate,logroller,可以自定义时区,可以压缩日志,配合tracing很好用(有几个其他的也试过,不是文件名改的奇奇怪怪就是这样那样的问题,这个是最好用的)。按时区滚动分割日志这个需求有人提出几年了,作者说不想引入time或者chrono这些库,因为他们会更新,那加一个i64的秒偏移量选项是有那么难吗?我不能李姐;
2025-06-12 更新,由于tracing用了重写的包导致清理过期日志有问题(测试在windows下似乎没问题),而且不能压缩备份,尝试更换为fastlog,试了下严重影响性能(接口返回时间增长了5倍左右)换回了tracing,还试了下tklog,还是老样子,无法输出包路径,只能输出文件路径,看起来很奇怪;
1. 引子
最近想要迁移一部分java应用至rust,在实际体验了tklog,log4rs,和tracing三款日志框架后,最后选用了tracing,log4rs的文件备份文件名没有时间,不便于管理,tklog的功能稍显简陋,在使用uselog()后会将某些底层包日志输出至终端,即使过滤掉某第三方个包(假设叫A)之后,这个包依赖的第三方包(A依赖的第三方包B)的日志依旧会被显示,但是tklog本身又无法显示包名,只能显示文件名,根本不知道这个日志是从哪个包漏出来的XD,最后在经历多次尝试后最终选定了使用最广泛的tracing!
2. 加入tracing包
#使用log门面
log = "0.4"
#本体
tracing = "0.1"
#滚动日志,package里的是文件滚动的增强,原版的只使用utc+0配置,非utc+0时区的日志时间文件名不正确。说一个好好笑的事情,这个问题存在3年了,有人提了issue给维护者了,维护者考虑再三说想用chrono的时间替代原来的,但是因为chrono好久没更新了,有个cve漏洞没修,所以就一直放着,一直不改,怎么说呢,没勺子不能用筷子吃饭吗?加一个手动设置时间偏移量的函数不行吗?ememem...
tracing-appender = { package = "tracing-appender-plus", version = "0.2", features = ["local-time",] }
#日志订阅,tracing默认不打印日志,需要订阅才会打印
tracing-subscriber = { version = "0.3", features = ["time", "env-filter"] }
#时间包,用来格式化日志和终端的时间显示
time = { version = "0.3", features = ["macros"] }
-----------------分割线------------------
#引入fastlog包
fast_log = {version = "1.7", features = ["zip"]}
fastdate = "0"
-----------------分割线------------------
logroller ={version = "0.1", features = ["tracing"]}
chrono ="0.4" #设置时区用了chrono库,需要引入
3. 上代码
use chrono::FixedOffset;
use logroller::{Compression, LogRollerBuilder, Rotation, RotationAge, TimeZone};
use time::macros::{format_description, offset};
use tracing_appender::
non_blocking::WorkerGuard
;
use tracing_subscriber::{
EnvFilter,
fmt::{time::OffsetTime, writer::MakeWriterExt},
};
pub fn tracing_init() -> (WorkerGuard, WorkerGuard) {
//格式化时间
let time_fmt =
format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]");//精确到毫秒
let timer = OffsetTime::new(offset!(+8), time_fmt);
//创建滚动日志
let file_appender = LogRollerBuilder::new("./logs", "service.log")
.rotation(Rotation::AgeBased(RotationAge::Daily)) // Rotate daily
.max_keep_files(2) // Keep a week's worth of logs
.time_zone(TimeZone::Fix(FixedOffset::east_opt(8 * 3600).unwrap())) // Use local timezone
.compression(Compression::Gzip) // Compress old logs
.build()
.expect("failed to initialize rolling file appender");
//使用非阻塞输出
let (stdout, guard1) = tracing_appender::non_blocking(std::io::stdout());//异步输出到stdout
let (file, guard2) = tracing_appender::non_blocking(file_appender);//异步输出到文件
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from(
"warn,actix_web1=debug,nacos_rust_client=info",
))//这里可以自定义某些第三方库的日志是否打印,第一个是全局日志等级,后面的是自定义包
.with_line_number(true)//显示行数
.with_thread_ids(true)//显示线程id
.with_ansi(false)//是否添加颜色信息,对于不支持颜色文本查看器日志文件会有乱码
.with_timer(timer)//设置时间格式化样式
.with_writer(file.and(stdout))//设置输出
.init();//初始化
(guard1, guard2)//一定要在主函数持有这两个异步缓冲区,否则无法输出
//2024-12-24 更新,主函数接收时不要使用"_",会被丢弃导致无法输出,使用"_a" 这样带有名字的接收才可以!!!
}
-----------------分割线------------------
// use fast_log::{
// Config,
// appender::{Command, FastLogRecord, RecordFormat},
// plugin::{
// file_split::{DateType, KeepType, Rolling, RollingType},
// packer::ZipPacker,
// },
// };
// use fastdate::DateTime;
// use log::LevelFilter;
// pub fn init_fast_log() {
// fast_log::init(
// Config::new()
// .chan_len(Some(100000))
// .console()
// .level(LevelFilter::Info)
// .file_split(
// "./logs/sevice.log",
// Rolling::new(RollingType::ByDate(DateType::Day)),
// KeepType::KeepNum(30),
// ZipPacker {},
// )
// .format(CustomFormate {}),
// )
// .unwrap();
// }
// struct CustomFormate {}
// impl RecordFormat for CustomFormate {
// fn do_format(&self, arg: &mut FastLogRecord) {
// match &arg.command {
// Command::CommandRecord => {
// let now = DateTime::from_system_time(arg.now, DateTime::now().offset()).set_offset(8 * 60 * 60)
// .format("YYYY-MM-DD hh:mm:ss.000000");
// arg.formated = format!(
// "{} [{:>5}] {}[{}]:{} {}\n",
// now,
// arg.level,
// arg.module_path,
// arg.line.unwrap_or_default(),
// arg.target,
// arg.args,
// );
// }
// Command::CommandExit => {}
// Command::CommandFlush(_) => {}
// }
// }
// }
//同时也试了下面官方文档的方法,会输出双份的日志,一份默认的,一份自定义的,不知道哪里不对
// struct CustomLog {}
// impl LogAppender for CustomLog {
// fn do_logs(&mut self, records: &[FastLogRecord]) {
// for record in records {
// let now = DateTime::from_system_time(record.now, DateTime::now().offset())
// .format("YYYY-MM-DD hh:mm:ss.000000+08:00");
// let data = format!(
// "{} [{:>5}] {}:{} {}\n",
// now,
// record.level,
// record.module_path,
// record.line.unwrap_or_default(),
// record.args,
// );
// print!("{}", data);
// }
// }
// }