refactor: remove loco-rs deps

This commit is contained in:
2025-03-01 15:21:14 +08:00
parent a68aab1452
commit 2844e1fc32
66 changed files with 2565 additions and 1876 deletions

View File

@@ -0,0 +1,38 @@
use serde::{Deserialize, Serialize};
use super::{
LogRotation,
core::{LogFormat, LogLevel},
};
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct LoggerConfig {
pub enable: bool,
#[serde(default)]
pub pretty_backtrace: bool,
pub level: LogLevel,
pub format: LogFormat,
pub filter: Option<String>,
pub override_filter: Option<String>,
pub file_appender: Option<LoggerFileAppender>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct LoggerFileAppender {
pub enable: bool,
#[serde(default)]
pub non_blocking: bool,
pub level: LogLevel,
pub format: LogFormat,
pub rotation: LogRotation,
pub dir: Option<String>,
pub filename_prefix: Option<String>,
pub filename_suffix: Option<String>,
pub max_log_files: usize,
}

View File

@@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};
use serde_variant::to_variant_name;
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub enum LogLevel {
#[serde(rename = "off")]
Off,
#[serde(rename = "trace")]
Trace,
#[serde(rename = "debug")]
Debug,
#[serde(rename = "info")]
#[default]
Info,
#[serde(rename = "warn")]
Warn,
#[serde(rename = "error")]
Error,
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
to_variant_name(self).expect("only enum supported").fmt(f)
}
}
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub enum LogFormat {
#[serde(rename = "compact")]
#[default]
Compact,
#[serde(rename = "pretty")]
Pretty,
#[serde(rename = "json")]
Json,
}
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub enum LogRotation {
#[serde(rename = "minutely")]
Minutely,
#[serde(rename = "hourly")]
#[default]
Hourly,
#[serde(rename = "daily")]
Daily,
#[serde(rename = "never")]
Never,
}

View File

@@ -0,0 +1,8 @@
pub mod config;
pub mod core;
pub mod service;
pub use core::{LogFormat, LogLevel, LogRotation};
pub use config::{LoggerConfig, LoggerFileAppender};
pub use service::LoggerService;

View File

@@ -0,0 +1,162 @@
use std::sync::OnceLock;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{
EnvFilter, Layer, Registry,
fmt::{self, MakeWriter},
layer::SubscriberExt,
util::SubscriberInitExt,
};
use super::{LogFormat, LogLevel, LogRotation, LoggerConfig};
use crate::errors::{RError, RResult};
// Function to initialize the logger based on the provided configuration
const MODULE_WHITELIST: &[&str] = &["sea_orm_migration", "tower_http", "sqlx::query", "sidekiq"];
// Keep nonblocking file appender work guard
static NONBLOCKING_WORK_GUARD_KEEP: OnceLock<WorkerGuard> = OnceLock::new();
pub struct LoggerService {}
impl LoggerService {
pub fn init_layer<W2>(
make_writer: W2,
format: &LogFormat,
ansi: bool,
) -> Box<dyn Layer<Registry> + Sync + Send>
where
W2: for<'writer> MakeWriter<'writer> + Sync + Send + 'static,
{
match format {
LogFormat::Compact => fmt::Layer::default()
.with_ansi(ansi)
.with_writer(make_writer)
.compact()
.boxed(),
LogFormat::Pretty => fmt::Layer::default()
.with_ansi(ansi)
.with_writer(make_writer)
.pretty()
.boxed(),
LogFormat::Json => fmt::Layer::default()
.with_ansi(ansi)
.with_writer(make_writer)
.json()
.boxed(),
}
}
fn init_env_filter(override_filter: Option<&String>, level: &LogLevel) -> EnvFilter {
EnvFilter::try_from_default_env()
.or_else(|_| {
// user wanted a specific filter, don't care about our internal whitelist
// or, if no override give them the default whitelisted filter (most common)
override_filter.map_or_else(
|| {
EnvFilter::try_new(
MODULE_WHITELIST
.iter()
.map(|m| format!("{m}={level}"))
.chain(std::iter::once(format!(
"{}={}",
env!("CARGO_CRATE_NAME"),
level
)))
.collect::<Vec<_>>()
.join(","),
)
},
EnvFilter::try_new,
)
})
.expect("logger initialization failed")
}
pub async fn from_config(config: LoggerConfig) -> RResult<Self> {
let mut layers: Vec<Box<dyn Layer<Registry> + Sync + Send>> = Vec::new();
if let Some(file_appender_config) = config.file_appender.as_ref() {
if file_appender_config.enable {
let dir = file_appender_config
.dir
.as_ref()
.map_or_else(|| "./logs".to_string(), ToString::to_string);
let mut rolling_builder = tracing_appender::rolling::Builder::default()
.max_log_files(file_appender_config.max_log_files);
rolling_builder = match file_appender_config.rotation {
LogRotation::Minutely => {
rolling_builder.rotation(tracing_appender::rolling::Rotation::MINUTELY)
}
LogRotation::Hourly => {
rolling_builder.rotation(tracing_appender::rolling::Rotation::HOURLY)
}
LogRotation::Daily => {
rolling_builder.rotation(tracing_appender::rolling::Rotation::DAILY)
}
LogRotation::Never => {
rolling_builder.rotation(tracing_appender::rolling::Rotation::NEVER)
}
};
let file_appender = rolling_builder
.filename_prefix(
file_appender_config
.filename_prefix
.as_ref()
.map_or_else(String::new, ToString::to_string),
)
.filename_suffix(
file_appender_config
.filename_suffix
.as_ref()
.map_or_else(String::new, ToString::to_string),
)
.build(dir)?;
let file_appender_layer = if file_appender_config.non_blocking {
let (non_blocking_file_appender, work_guard) =
tracing_appender::non_blocking(file_appender);
NONBLOCKING_WORK_GUARD_KEEP
.set(work_guard)
.map_err(|_| RError::CustomMessageStr("cannot lock for appender"))?;
Self::init_layer(
non_blocking_file_appender,
&file_appender_config.format,
false,
)
} else {
Self::init_layer(file_appender, &file_appender_config.format, false)
};
layers.push(file_appender_layer);
}
}
if config.enable {
let stdout_layer = Self::init_layer(std::io::stdout, &config.format, true);
layers.push(stdout_layer);
}
if !layers.is_empty() {
let env_filter = Self::init_env_filter(config.override_filter.as_ref(), &config.level);
tracing_subscriber::registry()
.with(layers)
.with(env_filter)
.init();
}
if config.pretty_backtrace {
unsafe {
std::env::set_var("RUST_BACKTRACE", "1");
}
tracing::warn!(
"pretty backtraces are enabled (this is great for development but has a runtime \
cost for production. disable with `logger.pretty_backtrace` in your config yaml)"
);
}
Ok(Self {})
}
}