Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

doe

Package Overview
Dependencies
Maintainers
1
Versions
254
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

doe - npm Package Compare versions

Comparing version
1.1.76
to
1.1.77
+1
-1
.cargo_vcs_info.json
{
"git": {
"sha1": "2e8e8d58814ebd01371138475a9da123ef21fcd7"
"sha1": "ebd7cd3938bafd14ea194ecdf881e474f5d5e4b0"
},
"path_in_vcs": ""
}

@@ -15,3 +15,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO

name = "doe"
version = "1.1.76"
version = "1.1.77"
authors = ["Andrew <dnrops@outlook.com>"]

@@ -18,0 +18,0 @@ build = false

@@ -0,0 +0,0 @@ doe v1.1.66 (/mnt/e/CODE/gitlab_code/doe_gitlink)

/// logger mod
///
/// # Examples
///
/// ## Basic usage
/// ```ignore
/// fn main() {
/// use doe::logger::*;
/// doe::logger::init();
/// debug!("hello world");
/// info!("hello world");
/// warn!("hello world");
/// }
/// use doe::logger::*;
/// doe::logger::init();
/// debug!("hello world");
/// info!("hello world");
/// warn!("hello world");
/// ```
///
///
/// ## Using builder pattern (fluent interface)
/// ```ignore
/// use doe::logger::*;
///
/// // Custom configuration with fluent interface
/// LoggerBuilder::init()
/// .with("debug")
/// .log_dir("logs")
/// .filename_prefix("myapp")
/// .filename_suffix("log")
/// .file_appender_rotation(doe::logger::tracing_appender::rolling::Rotation::DAILY)
/// .with_console(true)
/// .with_file(true)
/// .retention_days(30)
/// .build();
///
/// info!("Logger initialized with custom configuration");
/// ```
///
/// ## Console only
/// ```ignore
/// use doe::logger::*;
///
/// LoggerBuilder::init()
/// .with("info")
/// .with_console(true)
/// .with_file(false)
/// .build();
///
/// info!("Only console logging enabled");
/// ```
///
/// ## File only
/// ```ignore
/// use doe::logger::*;
///
/// LoggerBuilder::init()
/// .with("debug")
/// .log_dir("logs")
/// .with_console(false)
/// .with_file(true)
/// .build();
///
/// info!("Only file logging enabled");
/// ```
#[allow(warnings)]
#[cfg(feature = "logger")]
pub mod logger {
use chrono::{Local, NaiveDate, TimeDelta, TimeZone};
use std::fs;
use std::path::{Path, PathBuf};
use time::{format_description::parse, UtcOffset};
use tracing_appender::{non_blocking, rolling};
use tracing_error::ErrorLayer;
use tracing_subscriber::{
filter::EnvFilter,
fmt,
fmt::time::OffsetTime,
layer::SubscriberExt,
registry::Registry,
util::SubscriberInitExt,
};
// 重新导出tracing的宏,保持原有使用方式
pub use tracing::*;
pub use tracing_appender::*;
pub use tracing_appender;
pub use tracing_subscriber::*;
pub use tracing_subscriber;
pub use tracing_error::*;
pub use tracing_error;
/// 默认日志保留天数
const DEFAULT_LOG_RETENTION_DAYS: u64 = 14;
/// 初始化debug级别的日志系统
/// 自动清理超过14天的旧日志文件
pub fn init_debug() {
// 导入tracing_subscriber库中的模块,用于日志过滤和格式化
use tracing_subscriber::{filter::EnvFilter, fmt, layer::SubscriberExt, Registry};
// 清理旧日志文件
if let Err(e) = clean_old_logs("log", "app", "log", rolling::Rotation::DAILY, DEFAULT_LOG_RETENTION_DAYS) {
eprintln!("Warning: Failed to clean old logs: {}", e);
}
// 尝试从环境变量中解析日志级别过滤器,如果失败则默认为"info"级别
let env_filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("debug"));
use chrono::Local;
use time::UtcOffset;
use tracing_subscriber::fmt::time::OffsetTime;
let offset = UtcOffset::current_local_offset().expect("should get local offset!");
let time_format =
time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")
.expect("format string should be valid!");
let timer = OffsetTime::new(offset, time_format);
// 创建一个格式化日志层,使用本地时间格式化日志,并将日志输出到标准错误流
// 初始化日志配置
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("debug"));
let timer = create_local_timer();
// 控制台日志层
let formatting_layer = fmt::layer()

@@ -38,16 +109,10 @@ .with_timer(timer.clone())

// 导入滚动日志文件追加器,用于创建日志文件
use tracing_appender::rolling::{RollingFileAppender, Rotation};
// 创建一个滚动日志文件追加器,日志文件名基于日期旋转,但在这里设置为从不旋转
let file_appender = RollingFileAppender::builder()
.rotation(Rotation::DAILY)
// 文件日志层
let file_appender = rolling::RollingFileAppender::builder()
.rotation(rolling::Rotation::DAILY)
.filename_prefix("app")
.filename_suffix("log")
.build("log")
.expect("initializing rolling file appender failed");
let stdout = std::io::stdout.with_max_level(tracing::Level::INFO);
// 创建一个文件日志层,禁用ANSI颜色,并使用非阻塞模式写入日志文件
.expect("Failed to create rolling file appender");
let file_layer = fmt::layer()

@@ -58,39 +123,35 @@ .with_ansi(false)

// 将默认的Registry与环境变量过滤器、格式化日志层和文件日志层组合,并初始化
let registy = Registry::default()
// 组合并初始化日志系统
if let Err(e) = Registry::default()
.with(env_filter)
.with(formatting_layer)
.with(file_layer);
registy.max_level_hint();
let _ = tracing::subscriber::set_global_default(registy);
.with(file_layer)
.try_init()
{
eprintln!("Failed to initialize debug logger: {}", e);
}
}
/// 初始化info级别的日志系统
/// 自动清理超过14天的旧日志文件
pub fn init_info() {
// 清理旧日志文件
if let Err(e) = clean_old_logs("log", "app", "log", rolling::Rotation::DAILY, DEFAULT_LOG_RETENTION_DAYS) {
eprintln!("Warning: Failed to clean old logs: {}", e);
}
pub fn init_info() {
use tracing_subscriber::{filter::EnvFilter, fmt, layer::SubscriberExt, Registry};
use chrono::Local;
use time::UtcOffset;
use tracing_subscriber::fmt::time::OffsetTime;
// 从环境变量中解析日志级别过滤器,默认为 "info"
let env_filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
// 设置本地时间格式
let offset = UtcOffset::current_local_offset().expect("should get local offset!");
let time_format =
time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")
.expect("format string should be valid!");
let timer = OffsetTime::new(offset, time_format);
// 创建一个滚动日志文件追加器
use tracing_appender::rolling::{RollingFileAppender, Rotation};
let file_appender = RollingFileAppender::builder()
.rotation(Rotation::DAILY)
// 初始化日志配置
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info"));
let timer = create_local_timer();
// 文件日志层
let file_appender = rolling::RollingFileAppender::builder()
.rotation(rolling::Rotation::DAILY)
.filename_prefix("app")
.filename_suffix("log")
.build("log")
.expect("initializing rolling file appender failed");
// 创建一个文件日志层,禁用ANSI颜色,并使用非阻塞模式写入日志文件
.expect("Failed to create rolling file appender");
let file_layer = fmt::layer()

@@ -100,80 +161,353 @@ .with_ansi(false)

.with_writer(file_appender);
// 控制台日志层
let formatting_layer = fmt::layer()
.with_timer(timer.clone())
.pretty()
.with_writer(std::io::stdout);
.with_timer(timer)
.pretty()
.with_writer(std::io::stdout);
// 将默认的Registry与环境变量过滤器、控制台日志层和文件日志层组合,并初始化
// 将默认的Registry与环境变量过滤器、格式化日志层和文件日志层组合,并初始化
let registy = Registry::default()
// 组合并初始化日志系统
if let Err(e) = Registry::default()
.with(env_filter)
.with(formatting_layer)
.with(file_layer);
registy.max_level_hint();
let _ = tracing::subscriber::set_global_default(registy);
.with(file_layer)
.try_init()
{
eprintln!("Failed to initialize info logger: {}", e);
}
}
use tracing_appender::{non_blocking, rolling};
use tracing_error::ErrorLayer;
use tracing_subscriber::fmt::writer::MakeWriterExt;
use tracing_subscriber::{
filter::EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt, Registry,
};
/// Initializes the logger with the given configuration.
///
/// # Arguments
///
/// * `log_level` - The log level to use. Defaults to "info" if not provided.
/// * `log_file_path` - The path to the log file. If `None`, logs will only go to stdout.
/// * `pretty_print` - Whether to use pretty printing for logs. Defaults to `false`.
///
/// # Example
///
/// ```rust
/// logger::init_with_option(Some("debug"), Some("logs/app.log"));
/// ```
pub fn init_with_option(log_level: Option<&str>, log_file_path: Option<&str>) {
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(log_level.unwrap_or("info")));
/// Logger 构建器,提供 fluent interface
pub struct LoggerBuilder {
log_level: String,
log_dir: String,
filename_prefix: String,
filename_suffix: String,
rotation: Option<rolling::Rotation>,
console_enabled: bool,
file_enabled: bool,
retention_days: u64,
}
// Create a formatting layer for stderr
let formatting_layer = fmt::layer()
.with_timer(tracing_subscriber::fmt::time::time())
.with_level(true)
.with_file(true)
.with_writer(std::io::stderr);
impl Default for LoggerBuilder {
fn default() -> Self {
Self {
log_level: "info".to_string(),
log_dir: "log".to_string(),
filename_prefix: "app".to_string(),
filename_suffix: "log".to_string(),
rotation: Some(rolling::Rotation::DAILY),
console_enabled: true,
file_enabled: true,
retention_days: DEFAULT_LOG_RETENTION_DAYS,
}
}
}
let formatting_layer = formatting_layer.pretty();
impl LoggerBuilder {
/// 创建一个新的 Logger 构建器
pub fn init() -> Self {
Self::default()
}
let subscriber = Registry::default()
.with(env_filter)
.with(ErrorLayer::default())
.with(formatting_layer);
/// 设置日志级别
pub fn with(mut self, level: &str) -> Self {
self.log_level = level.to_string();
self
}
if let Some(path) = log_file_path {
let file_appender = rolling::never("logs", path);
let (non_blocking_appender, guard) = non_blocking(file_appender);
/// 设置日志目录
pub fn log_dir(mut self, dir: &str) -> Self {
self.log_dir = dir.to_string();
self
}
let file_layer = fmt::layer()
.with_ansi(false)
.with_writer(non_blocking_appender);
/// 设置文件名前缀
pub fn filename_prefix(mut self, prefix: &str) -> Self {
self.filename_prefix = prefix.to_string();
self
}
let subscriber = subscriber.with(file_layer);
/// 设置文件名后缀
pub fn filename_suffix(mut self, suffix: &str) -> Self {
self.filename_suffix = suffix.to_string();
self
}
// Initialize the subscriber
tracing::subscriber::set_global_default(subscriber)
.expect("Failed to set global subscriber");
/// 设置日志轮转策略
pub fn file_appender_rotation(mut self, rotation: rolling::Rotation) -> Self {
self.rotation = Some(rotation);
self
}
// Ensure all logs are flushed before the application exits
std::mem::drop(guard);
} else {
// Initialize the subscriber without file logging
tracing::subscriber::set_global_default(subscriber)
.expect("Failed to set global subscriber");
/// 启用或禁用控制台输出
pub fn with_console(mut self, enabled: bool) -> Self {
self.console_enabled = enabled;
self
}
/// 启用或禁用文件输出
pub fn with_file(mut self, enabled: bool) -> Self {
self.file_enabled = enabled;
self
}
/// 设置日志保留天数
pub fn retention_days(mut self, days: u64) -> Self {
self.retention_days = days;
self
}
/// 构建并初始化日志系统
pub fn build(self) {
// 清理旧日志文件
if self.file_enabled {
let rotation = self.rotation.as_ref().unwrap_or(&rolling::Rotation::NEVER);
if let Err(e) = clean_old_logs(
&self.log_dir,
&self.filename_prefix,
&self.filename_suffix,
rotation.clone(),
self.retention_days
) {
eprintln!("Warning: Failed to clean old logs: {}", e);
}
}
// 创建环境过滤器
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(&self.log_level));
let timer = create_local_timer();
// 根据配置构建不同的 subscriber
match (self.console_enabled, self.file_enabled) {
(true, true) => {
// 控制台和文件都启用
let file_appender = if let Some(rotation) = self.rotation {
rolling::RollingFileAppender::builder()
.rotation(rotation)
.filename_prefix(&self.filename_prefix)
.filename_suffix(&self.filename_suffix)
.build(&self.log_dir)
.expect("Failed to create rolling file appender")
} else {
let file_path = format!("{}/{}.{}", self.log_dir, self.filename_prefix, self.filename_suffix);
rolling::never(&self.log_dir, &file_path)
};
let console_layer = fmt::layer()
.with_timer(timer.clone())
.pretty()
.with_writer(std::io::stdout);
let file_layer = fmt::layer()
.with_ansi(false)
.with_timer(timer)
.with_writer(file_appender);
if let Err(e) = Registry::default()
.with(env_filter)
.with(console_layer)
.with(file_layer)
.try_init()
{
eprintln!("Failed to initialize logger: {}", e);
}
}
(true, false) => {
// 只启用控制台
let console_layer = fmt::layer()
.with_timer(timer)
.pretty()
.with_writer(std::io::stdout);
if let Err(e) = Registry::default()
.with(env_filter)
.with(console_layer)
.try_init()
{
eprintln!("Failed to initialize logger: {}", e);
}
}
(false, true) => {
// 只启用文件
let file_appender = if let Some(rotation) = self.rotation {
rolling::RollingFileAppender::builder()
.rotation(rotation)
.filename_prefix(&self.filename_prefix)
.filename_suffix(&self.filename_suffix)
.build(&self.log_dir)
.expect("Failed to create rolling file appender")
} else {
let file_path = format!("{}/{}.{}", self.log_dir, self.filename_prefix, self.filename_suffix);
rolling::never(&self.log_dir, &file_path)
};
let file_layer = fmt::layer()
.with_ansi(false)
.with_timer(timer)
.with_writer(file_appender);
if let Err(e) = Registry::default()
.with(env_filter)
.with(file_layer)
.try_init()
{
eprintln!("Failed to initialize logger: {}", e);
}
}
(false, false) => {
eprintln!("Warning: Both console and file logging are disabled");
}
}
}
}
/// 创建本地时间格式化器(完全兼容版)
fn create_local_timer() -> OffsetTime<time::format_description::OwnedFormatItem> {
let offset = UtcOffset::current_local_offset()
.expect("Failed to get local time offset");
// 使用 parse 函数替代宏,兼容所有 time 库版本
let time_format = parse("[year]-[month]-[day] [hour]:[minute]:[second]")
.expect("Invalid time format string");
// 将 Vec<FormatItem> 转换为 OwnedFormatItem 以满足静态生命周期要求
let time_format = time::format_description::OwnedFormatItem::from(time_format);
OffsetTime::new(offset, time_format)
}
/// 清理指定目录下超过指定天数的日志文件
///
/// # 参数
/// - `log_dir`: 日志文件所在目录
/// - `filename_prefix`: 日志文件名前缀
/// - `filename_suffix`: 日志文件名后缀
/// - `file_appender_rotation`: 日志轮转策略
/// - `retention_days`: 保留的最大天数,超过该天数的文件将被删除
///
/// # 返回值
/// - Result<(), std::io::Error>: 成功返回Ok(()),失败返回错误信息
pub fn clean_old_logs(
log_dir: &str,
filename_prefix: &str,
filename_suffix: &str,
file_appender_rotation: rolling::Rotation,
retention_days: u64,
) -> Result<(), std::io::Error> {
// 检查日志目录是否存在
let log_dir_path = Path::new(log_dir);
if !log_dir_path.exists() {
return Ok(());
}
// 计算过期时间点
let cutoff_datetime = Local::now() - TimeDelta::days(retention_days as i64);
// 遍历日志目录下的所有文件
for entry in fs::read_dir(log_dir_path)? {
let entry = entry?;
let path = entry.path();
// 只处理文件(跳过目录)
if path.is_file() {
// 获取文件名
if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
// 检查是否匹配前缀和后缀
let has_correct_prefix = file_name.starts_with(&format!("{}.", filename_prefix));
let has_correct_suffix = file_name.ends_with(&format!(".{}", filename_suffix));
if !has_correct_prefix || !has_correct_suffix {
continue;
}
// 根据轮转策略判断文件是否过期
let is_expired = match file_appender_rotation {
rolling::Rotation::NEVER => {
// 不轮转,直接比较文件修改时间
if let Ok(metadata) = fs::metadata(&path) {
if let Ok(modified) = metadata.modified() {
let modified_datetime = chrono::DateTime::<chrono::Local>::from(modified);
modified_datetime < cutoff_datetime
} else {
false
}
} else {
false
}
}
rolling::Rotation::HOURLY => {
// 每小时轮转,文件名格式: prefix.YYYY-MM-DD-HH.suffix
match extract_datetime_from_filename(file_name, &format!("{}.", filename_prefix), &format!(".{}", filename_suffix), "%Y-%m-%d-%H") {
Some(file_datetime) => file_datetime < cutoff_datetime,
None => false,
}
}
rolling::Rotation::DAILY => {
// 每天轮转,文件名格式: prefix.YYYY-MM-DD.suffix
match extract_datetime_from_filename(file_name, &format!("{}.", filename_prefix), &format!(".{}", filename_suffix), "%Y-%m-%d") {
Some(file_datetime) => file_datetime < cutoff_datetime,
None => false,
}
}
rolling::Rotation::MINUTELY => {
// 每分钟轮转,文件名格式: prefix.YYYY-MM-DD-HH-MM.suffix
match extract_datetime_from_filename(file_name, &format!("{}.", filename_prefix), &format!(".{}", filename_suffix), "%Y-%m-%d-%H-%M") {
Some(file_datetime) => file_datetime < cutoff_datetime,
None => false,
}
}
rolling::Rotation::WEEKLY => {
// 每周轮转,使用文件修改时间
if let Ok(metadata) = fs::metadata(&path) {
if let Ok(modified) = metadata.modified() {
let modified_datetime = chrono::DateTime::<chrono::Local>::from(modified);
modified_datetime < cutoff_datetime
} else {
false
}
} else {
false
}
}
};
if is_expired {
println!("Deleting old log file: {}", path.display());
fs::remove_file(&path)?;
}
}
}
}
Ok(())
}
/// 从文件名中提取日期时间
fn extract_datetime_from_filename(
file_name: &str,
prefix: &str,
suffix: &str,
format_str: &str,
) -> Option<chrono::DateTime<Local>> {
// 移除前缀和后缀
let datetime_str = file_name.strip_prefix(prefix)?.strip_suffix(suffix)?;
// 尝试解析日期时间
match NaiveDate::parse_from_str(datetime_str, format_str) {
Ok(date) => Some(date.and_hms_opt(0, 0, 0)?.and_local_timezone(Local).unwrap()),
Err(_) => None,
}
}
/// 简化的初始化函数(兼容原有接口)
pub fn init() {
init_info();
}
}
#[cfg(feature = "logger")]
pub use logger::*;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet