最近再学RUST,练手项目选择 Web方向,写到日志这块的时候,发现两个库,一个是Log4rs,这个库使用还是挺方便的,但是我想实现日志按照日期切割就不行了,我再 github issuse 中发现有人提出来要支持文件按照日期命名,但是发现需求搁置了,所以我换了另一个库 tracing,但是文档和示例确实不太直观,查了多篇文章实现出来的功能,在这记录一下给有同样需求的同志们做个参考。直接上代码:
Cargo.toml
[dependencies]
chrono = "0.4.39"
colored = "3.0.0"
serde_json = "1.0.135"
tracing = "0.1.41"
tracing-appender = "0.2.3"
tracing-log = "0.2.0"
tracing-subscriber = {version = "0.3.19",features =["json"]}
use tracing_subscriber::{prelude::*};
use tracing_log::LogTracer;
use tracing::{info, span, Level};
use colored::Colorize;
use tracing_subscriber::fmt::time::{FormatTime};
use tracing_subscriber::fmt::format::Writer;
use chrono::Local;
use tracing::instrument::WithSubscriber;
use tracing_appender::non_blocking::WorkerGuard;
struct LocalTimer;
const fn east8() -> Option<chrono::FixedOffset> {
chrono::FixedOffset::east_opt(8 * 3600)
}
impl FormatTime for LocalTimer {
fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
let now = chrono::Utc::now().with_timezone(&east8().unwrap());
write!(w, "{}", now.format("%Y-%m-%d %H:%M:%S"))
}
}
struct LogTarget{}
impl LogTarget {
pub const WEB_LOG:&'static str = "web-log";
pub const API_LOG:&'static str = "api-log";
}
fn main() {
// 日志配置初始化
// 这里需要接收 guard 否则文件输出内容会是空的
let _guard = init_log();
info!(target: LogTarget::API_LOG,"api log message");
info!(target: LogTarget::WEB_LOG,"web log message");
info!("hello world");
}
///
/// 日志配置初始化
///
fn init_log() -> Vec<WorkerGuard> {
let mut guards = Vec::new();
// 消费log门面日志 转为 tracing Event日志
LogTracer::builder()
// .with_max_level(log::LevelFilter::Error)
.init()
.expect("[PEAR] LogTracer 初始化失败");
// 输出日志
let console_subscriber = tracing_subscriber::fmt
::layer().with_writer(std::io::stdout).with_timer(LocalTimer);
// 特别定制web日志输出
let file_appender = tracing_appender::rolling
::daily("logs/", LogTarget::WEB_LOG);
// 生成非阻塞写入器
let (non_blocking, guard1) = tracing_appender
::non_blocking(file_appender);
guards.push(guard1);
let file_subscriber = tracing_subscriber::fmt::layer().json()
.with_writer(non_blocking)
.with_ansi(false)
.with_timer(LocalTimer)
.with_filter(tracing_subscriber::filter::filter_fn(|metadata| {
// Only enable spans or events with the target "interesting_things"
metadata.target() == LogTarget::WEB_LOG
}));
// 特别定制api日志输出
let file_appender_api = tracing_appender::rolling
::daily("logs/", LogTarget::API_LOG);
// 生成非阻塞写入器
let (non_blocking_api, guard) = tracing_appender
::non_blocking(file_appender_api);
guards.push(guard);
let file_subscriber_api = tracing_subscriber::fmt::layer().json()
.with_writer(non_blocking_api)
.with_ansi(false)
.with_timer(LocalTimer)
.with_filter(tracing_subscriber::filter::filter_fn(|metadata| {
// Only enable spans or events with the target "interesting_things"
metadata.target() == LogTarget::API_LOG
}));
// 集合
let subscriber = tracing_subscriber::registry()
.with(console_subscriber)
.with(file_subscriber)
.with(file_subscriber_api);
tracing::subscriber::set_global_default(subscriber)
.expect("setting default subscriber failed");
guards
}
后期的其他一些模块的用法和代码会写到这个项目里,有兴趣的可以关注一下:GitHub - gphper/rustadmin: Rust 开发脚手架