- [后端/PDF解析] 重构 MinerU PDF 解析流程:引入预签名两阶段直传机制,解决大文件 API 传输限制问题;支持轮询机制与本地 images 备用目录存储。 - [后端/同步与下载] 新增经典 ADS SCAN 扫描件 PDF 和 ADS_PDF 直接通道的下载逻辑;新增常用同步检索配置的持久化存储与去重管理 API。 - [后端/日志] 重构日志系统,支持控制台 pretty 输出与每日滚动文件日志(使用上海 +08:00 时区),引入 HTTP 路由请求链路追踪。 - [前端/引力图] 升级引用星系图 canvas 交互:支持平移拖拽与滚轮缩放,添加引力圈轨道装饰及未导入文献的半透明视觉区分。 - [前端/控制台] 统一重构为扁平高对比度浅色纯中文控制台样式;重新设计文献详情弹窗与状态进度条。 - [数据库] 新增 papers 表的 doctype 字段及 sync_queries 检索配置表。
85 lines
2.8 KiB
Rust
85 lines
2.8 KiB
Rust
// src/services/logging.rs
|
|
use chrono::{DateTime, FixedOffset, Utc};
|
|
use std::env;
|
|
use std::fs;
|
|
use tracing_appender::non_blocking::WorkerGuard;
|
|
use tracing_appender::rolling;
|
|
use tracing_subscriber::fmt::format::Writer;
|
|
use tracing_subscriber::fmt::time::FormatTime;
|
|
use tracing_subscriber::{
|
|
fmt, layer::SubscriberExt, util::SubscriberInitExt,
|
|
EnvFilter, Layer,
|
|
};
|
|
|
|
pub struct ShanghaiTime;
|
|
|
|
impl FormatTime for ShanghaiTime {
|
|
fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
|
|
let now: DateTime<Utc> = Utc::now();
|
|
let offset = FixedOffset::east_opt(8 * 3600).unwrap();
|
|
let shanghai_time = now.with_timezone(&offset);
|
|
write!(w, "{}", shanghai_time.format("%Y-%m-%dT%H:%M:%S%.3f%:z"))
|
|
}
|
|
}
|
|
|
|
/// 初始化系统全局日志模块,支持控制台输出与每天自动滚动的日志文件
|
|
pub fn init_logging() -> anyhow::Result<Vec<WorkerGuard>> {
|
|
let mut guards = Vec::new();
|
|
|
|
// 从环境变量中读取配置
|
|
let log_level = env::var("LOG_LEVEL").unwrap_or_else(|_| "info,astroresearch=debug".to_string());
|
|
let log_format = env::var("LOG_FORMAT").unwrap_or_else(|_| "pretty".to_string());
|
|
let log_outputs = env::var("LOG_OUTPUTS").unwrap_or_else(|_| "stdout,file".to_string());
|
|
|
|
let env_filter = EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| EnvFilter::new(&log_level));
|
|
let is_json = log_format.to_lowercase() == "json";
|
|
|
|
let mut layers: Vec<Box<dyn Layer<tracing_subscriber::Registry> + Send + Sync>> = Vec::new();
|
|
|
|
// 1. 控制台输出层 (stdout)
|
|
if log_outputs.contains("stdout") {
|
|
let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());
|
|
guards.push(guard);
|
|
|
|
let fmt_layer = fmt::layer()
|
|
.with_timer(ShanghaiTime)
|
|
.with_writer(non_blocking);
|
|
|
|
if is_json {
|
|
layers.push(fmt_layer.json().with_ansi(false).boxed());
|
|
} else {
|
|
layers.push(fmt_layer.pretty().with_ansi(true).boxed());
|
|
}
|
|
}
|
|
|
|
// 2. 每日滚动文件日志层 (file)
|
|
if log_outputs.contains("file") {
|
|
let log_dir = env::var("LOG_DIR").unwrap_or_else(|_| "logs".to_string());
|
|
fs::create_dir_all(&log_dir).unwrap_or(());
|
|
|
|
let file_appender = rolling::daily(log_dir, "astro_research.log");
|
|
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
|
guards.push(guard);
|
|
|
|
let fmt_layer = fmt::layer()
|
|
.with_timer(ShanghaiTime)
|
|
.with_writer(non_blocking)
|
|
.with_ansi(false);
|
|
|
|
if is_json {
|
|
layers.push(fmt_layer.json().boxed());
|
|
} else {
|
|
layers.push(fmt_layer.boxed());
|
|
}
|
|
}
|
|
|
|
// 3. 注册全部日志层
|
|
tracing_subscriber::registry()
|
|
.with(layers)
|
|
.with(env_filter)
|
|
.init();
|
|
|
|
Ok(guards)
|
|
}
|