refactor(core,shim): 统一基于 thiserror 的错误体系
- 移除 anyhow,使用 thiserror 定义强类型 Error 枚举 - 支持 #[from] 自动转换 mlua::Error,关键路径保留源码与上下文 - LuaRuntimeBuilder 采用惰性错误收集,简化链式初始化调用 - MirrorRegistry 实现规范化 FromLuaConversionError 错误转换
This commit is contained in:
+65
-63
@@ -1,7 +1,7 @@
|
||||
use crate::Layout;
|
||||
use serde::Deserialize;
|
||||
use std::convert::Infallible;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use tracing_appender::non_blocking::{NonBlocking, WorkerGuard};
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
@@ -61,41 +61,50 @@ impl FromStr for LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Logger {
|
||||
#[serde(default)]
|
||||
/// 日志配置模型(对应 mirror.ini 的纯强类型表示)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct LogConfig {
|
||||
pub level: LogLevel,
|
||||
pub log_dir: Option<PathBuf>,
|
||||
}
|
||||
impl Logger {
|
||||
impl LogConfig {
|
||||
/// 零依赖解析 ini / key-value 文本内容
|
||||
pub fn parse_ini(content: &str) -> Self {
|
||||
let mut logger = Self::default();
|
||||
/// 计算日志目录的实际物理路径(相对路径基于 base_dir 拼接)
|
||||
pub fn resolve_log_dir(&self, base_dir: &Path) -> PathBuf {
|
||||
match &self.log_dir {
|
||||
Some(p) if p.is_absolute() => p.clone(),
|
||||
Some(p) => base_dir.join(p),
|
||||
None => base_dir.join("logs"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// 协议 1: 反序列化 (INI 文本 -> LogConfig)
|
||||
impl FromStr for LogConfig {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(content: &str) -> Result<Self, Self::Err> {
|
||||
let mut config = Self::default();
|
||||
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
// 跳过空行、注释行 (# 或 ;) 和 section 头 ([...])
|
||||
if line.is_empty()
|
||||
|| line.starts_with('#')
|
||||
|| line.starts_with(';')
|
||||
|| line.starts_with('[')
|
||||
{
|
||||
// 跳过空行、注释 (#, ;) 与 section 标头 ([...])
|
||||
if line.is_empty() || line.starts_with(['#', ';', '[']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some((key, val)) = line.split_once('=') {
|
||||
let key = key.trim();
|
||||
let val = val.trim().trim_matches('"').trim_matches('\'');
|
||||
let val = val.trim().trim_matches(['"', '\'']);
|
||||
|
||||
match key {
|
||||
"level" => {
|
||||
if let Ok(lvl) = LogLevel::from_str(val) {
|
||||
logger.level = lvl;
|
||||
config.level = lvl;
|
||||
}
|
||||
}
|
||||
"log_dir" => {
|
||||
if !val.is_empty() {
|
||||
logger.log_dir = Some(PathBuf::from(val));
|
||||
config.log_dir = Some(PathBuf::from(val));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -103,9 +112,24 @@ impl Logger {
|
||||
}
|
||||
}
|
||||
|
||||
logger
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
// 协议 2: 序列化 (LogConfig -> INI 文本)
|
||||
impl std::fmt::Display for LogConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let dir_str = self
|
||||
.log_dir
|
||||
.as_deref()
|
||||
.and_then(|p| p.to_str())
|
||||
.unwrap_or("logs");
|
||||
|
||||
writeln!(f, "level = \"{}\"", self.level.as_str())?;
|
||||
writeln!(f, "log_dir = \"{}\"", dir_str)
|
||||
}
|
||||
}
|
||||
|
||||
/// 基于 Layout 自动寻址 mirror.toml 并初始化日志系统。
|
||||
/// 仅在开启详细日志(如 info/debug/trace)时激活异步文件记录并返回 `WorkerGuard`。
|
||||
pub fn init_logging_from(layout: &Layout) -> Option<WorkerGuard> {
|
||||
@@ -113,16 +137,11 @@ pub fn init_logging_from(layout: &Layout) -> Option<WorkerGuard> {
|
||||
// let config_path = layout.base_dir.join("mirror.toml");
|
||||
let config_path = layout.base_dir.join("mirror.ini");
|
||||
|
||||
// 读取配置文件(如不存在或解析失败,降级回退到默认设置)
|
||||
let config = if config_path.exists() {
|
||||
fs::read_to_string(&config_path)
|
||||
.map(|s| Logger::parse_ini(&s))
|
||||
// .ok()
|
||||
// .and_then(|s| toml::from_str::<Logger>(&s).ok())
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Logger::default()
|
||||
};
|
||||
// 1. 读取并反序列化配置文件(缺失或异常时安全降级为 Default)
|
||||
let config: LogConfig = fs::read_to_string(&config_path)
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
// 1. 优先读取环境变量 MIRROR_LOG_LEVEL,解析失败或未设置则退回到 config.level
|
||||
let effective_level = std::env::var("MIRROR_LOG_LEVEL")
|
||||
@@ -136,47 +155,30 @@ pub fn init_logging_from(layout: &Layout) -> Option<WorkerGuard> {
|
||||
// 基础终端输出层
|
||||
let stderr_layer = fmt::layer().with_writer(std::io::stderr).with_target(false);
|
||||
|
||||
// 2. 根据强类型枚举判断是否激活落盘模式
|
||||
if effective_level.is_verbose() {
|
||||
let (non_blocking, guard) = create_file_appender(layout, config.log_dir);
|
||||
let file_layer = fmt::layer()
|
||||
// 3. 动态配置落盘文件层(仅在 verbose 级别启用)
|
||||
let (file_layer, guard) = if effective_level.is_verbose() {
|
||||
let (non_blocking, guard) = create_file_appender(&config.resolve_log_dir(&layout.base_dir));
|
||||
let layer = fmt::layer()
|
||||
.with_writer(non_blocking)
|
||||
.with_ansi(false)
|
||||
.with_target(false);
|
||||
|
||||
let _ = tracing_subscriber::registry()
|
||||
.with(filter)
|
||||
.with(stderr_layer)
|
||||
.with(file_layer)
|
||||
.try_init();
|
||||
|
||||
Some(guard)
|
||||
(Some(layer), Some(guard))
|
||||
} else {
|
||||
let _ = tracing_subscriber::registry()
|
||||
.with(filter)
|
||||
.with(stderr_layer)
|
||||
.try_init();
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn create_file_appender(
|
||||
layout: &Layout,
|
||||
log_dir_override: Option<PathBuf>,
|
||||
) -> (NonBlocking, WorkerGuard) {
|
||||
let output_dir = match log_dir_override {
|
||||
Some(user_path) => {
|
||||
if user_path.is_relative() {
|
||||
layout.base_dir.join(user_path)
|
||||
} else {
|
||||
user_path
|
||||
}
|
||||
}
|
||||
None => layout.base_dir.join("logs"),
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir_all(&output_dir) {
|
||||
// 4. 统一组装并初始化 Subscriber(Option<Layer> 天然实现了 Layer trait)
|
||||
let _ = tracing_subscriber::registry()
|
||||
.with(filter)
|
||||
.with(stderr_layer)
|
||||
.with(file_layer)
|
||||
.try_init();
|
||||
|
||||
guard
|
||||
}
|
||||
|
||||
fn create_file_appender(output_dir: &Path) -> (NonBlocking, WorkerGuard) {
|
||||
if let Err(err) = fs::create_dir_all(output_dir) {
|
||||
eprintln!("[WARN] 创建日志目录 {} 失败: {}", output_dir.display(), err);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user