use crate::error::{LogError, LogResult};
use crate::logger::{CustomFilter, LogData, LogPlus};
use crate::utils::{AsyncLogType, debug_check_init, parse_level, parse_size};
use crate::quickwit::{QuickwitConfig, QuickwitClient};
use std::io::LineWriter;
use std::path::Path;
use std::sync::{mpsc, OnceLock};
static LOGGER_PLUS: OnceLock<LogPlus> = OnceLock::new();
#[inline]
pub fn init_log_simple(
level: &str,
log_file: String,
log_file_max: &str,
use_console: bool,
use_async: bool,
) -> LogResult<()> {
init_log_inner(
parse_level(level)?,
log_file,
parse_size(log_file_max)?,
use_console,
use_async,
None,
None,
)
}
#[inline]
pub fn init_log(
level: log::LevelFilter,
log_file: String,
log_file_max: u32,
use_console: bool,
use_async: bool,
) -> LogResult<()> {
init_log_inner(level, log_file, log_file_max, use_console, use_async, None, None)
}
#[inline]
pub fn init_log_with_plugin<P>(
level: log::LevelFilter,
log_file: String,
log_file_max: u32,
use_console: bool,
use_async: bool,
plugin: P,
) -> LogResult<()>
where
P: std::io::Write + Send + Sync + 'static,
{
init_log_inner(
level,
log_file,
log_file_max,
use_console,
use_async,
Some(Box::new(plugin)),
None,
)
}
#[inline]
pub fn init_log_with_filter(
level: log::LevelFilter,
log_file: String,
log_file_max: u32,
use_console: bool,
use_async: bool,
filter: impl CustomFilter,
) -> LogResult<()> {
init_log_inner(
level,
log_file,
log_file_max,
use_console,
use_async,
None,
Some(Box::new(filter)),
)
}
pub fn init_log_with_all<P>(
level: log::LevelFilter,
log_file: String,
log_file_max: u32,
use_console: bool,
use_async: bool,
plugin: P,
filter: impl CustomFilter,
) -> LogResult<()>
where
P: std::io::Write + Send + Sync + 'static,
{
init_log_inner(
level,
log_file,
log_file_max,
use_console,
use_async,
Some(Box::new(plugin)),
Some(Box::new(filter)),
)
}
pub fn set_level(target: String, level: log::LevelFilter) {
if let Ok(mut f) = get_logger_plus().level_filter.write() {
f.insert(target, level);
}
}
fn init_log_inner(
level: log::LevelFilter,
log_file: String,
log_file_max: u32,
use_console: bool,
use_async: bool,
plugin: Option<Box<dyn std::io::Write + Send + Sync + 'static>>,
filter: Option<Box<dyn CustomFilter>>,
) -> LogResult<()> {
init_log_inner_with_config(
level,
log_file,
log_file_max,
use_console,
use_async,
plugin,
filter,
true,
true,
true,
Vec::new(),
None,
)
}
pub fn init_log_inner_with_config(
level: log::LevelFilter,
log_file: String,
log_file_max: u32,
use_console: bool,
use_async: bool,
plugin: Option<Box<dyn std::io::Write + Send + Sync + 'static>>,
filter: Option<Box<dyn CustomFilter>>,
show_process_id: bool,
show_thread_info: bool,
show_module_path: bool,
highlight_keywords: Vec<String>,
quickwit_config: Option<QuickwitConfig>,
) -> LogResult<()> {
if log_file_max == 0 {
return Err(LogError::config("Max file size cannot be zero"));
}
if !log_file.is_empty() {
if let Some(parent) = Path::new(&log_file).parent() {
if !parent.exists() {
return Err(LogError::file_operation(
log_file.clone(),
"Parent directory does not exist",
));
}
}
}
debug_check_init();
log::set_max_level(level);
let console = if use_console {
Some(LineWriter::new(std::io::stdout()))
} else {
None
};
let (fileout, log_size) = if !log_file.is_empty() {
match crate::utils::open_log_file_sync(&log_file) {
Ok((writer, size)) => (Some(writer), size),
Err(e) => {
eprintln!("Failed to open log file {}: {}", log_file, e);
(None, 0)
}
}
} else {
(None, 0)
};
let sender = if use_async {
let (sender, receiver) = mpsc::channel::<AsyncLogType>();
let quickwit_config_clone = quickwit_config.clone();
std::thread::spawn(move || {
let quickwit_client = if let Some(config) = quickwit_config_clone {
match crate::quickwit::QuickwitClient::new(config) {
Ok(client) => {
if std::env::var("QUICKWIT_DEBUG").is_ok() {
eprintln!("✓ 异步线程中 Quickwit 客户端创建成功");
}
Some(client)
},
Err(e) => {
eprintln!("✗ 异步线程中 Quickwit 客户端创建失败: {}", e);
None
}
}
} else {
None
};
loop {
match receiver.recv() {
Ok(data) => match data {
AsyncLogType::Message(msg) => {
get_logger_plus().write(&msg);
crate::utils::put_msg_to_cache(msg);
}
AsyncLogType::Flush => get_logger_plus().flush_inner(),
AsyncLogType::QuickwitLog(log_entry) => {
if let Some(ref client) = quickwit_client {
match client.send_log(&log_entry) {
Ok(()) => {
if std::env::var("QUICKWIT_DEBUG").is_ok() {
eprintln!("✓ 异步模式:日志已发送到 Quickwit: {}", log_entry.message);
}
},
Err(e) => {
eprintln!("✗ 异步模式:发送日志到 Quickwit 失败: {}", e);
eprintln!(" 日志内容: {}", log_entry.message);
eprintln!(" 错误详情: {:?}", e);
}
}
}
}
},
Err(e) => eprintln!("logger channel recv error: {}", e),
}
}
});
Some(sender)
} else {
None
};
let logger_data = LogData {
log_size,
console,
fileout,
sender,
plugin,
};
let quickwit_client = match quickwit_config {
Some(config) => {
if std::env::var("QUICKWIT_DEBUG").is_ok() {
eprintln!("正在创建 Quickwit 客户端,配置: {:?}", config);
}
match QuickwitClient::new(config) {
Ok(client) => {
if std::env::var("QUICKWIT_DEBUG").is_ok() {
eprintln!("✓ Quickwit 客户端创建成功");
}
Some(client)
},
Err(e) => {
eprintln!("✗ Quickwit 客户端创建失败: {}", e);
return Err(e);
}
}
},
None => {
if std::env::var("QUICKWIT_DEBUG").is_ok() {
eprintln!("未配置 Quickwit,跳过客户端创建");
}
None
},
};
let logger = LogPlus::new(
level,
log_file,
log_file_max,
filter,
logger_data,
show_process_id,
show_thread_info,
show_module_path,
highlight_keywords,
quickwit_client,
);
LOGGER_PLUS
.set(logger)
.map_err(|_| LogError::initialization_error("Logger already initialized"))?;
log::set_logger(get_logger_plus())
.map_err(|e| LogError::initialization_error(format!("Failed to set logger: {}", e)))?;
Ok(())
}
pub fn get_logger_plus() -> &'static LogPlus {
LOGGER_PLUS.get().expect("Logger not initialized")
}
pub fn is_initialized() -> bool {
LOGGER_PLUS.get().is_some()
}
pub fn current_log_level() -> Option<log::LevelFilter> {
LOGGER_PLUS.get().map(|logger| logger.level)
}