Skip to main content

log_full/
logger.rs

1//! 核心日志器模块
2//! 
3//! 包含 LogPlus 日志器的实现和相关数据结构
4
5
6use crate::utils::{AsyncLogType, get_msg_from_cache, put_msg_to_cache, level_color};
7use crate::quickwit::{QuickwitClient, QuickwitLogEntry};
8use std::collections::{HashMap, VecDeque};
9use std::fs::File;
10use std::io::{LineWriter, Stdout, Write};
11use std::sync::{Mutex, RwLock, mpsc::Sender};
12
13/// 自定义过滤函数 trait
14pub trait CustomFilter: Send + Sync + 'static {
15    fn enabled(&self, record: &log::Record) -> bool;
16}
17
18/// 日志器数据
19pub struct LogData {
20    pub log_size: u32,
21    pub console: Option<LineWriter<Stdout>>,
22    pub fileout: Option<LineWriter<File>>,
23    pub sender: Option<Sender<AsyncLogType>>,
24    pub plugin: Option<Box<dyn std::io::Write + Send + Sync + 'static>>,
25}
26
27/// 核心日志器
28pub struct LogPlus {
29    pub level: log::LevelFilter,
30    pub log_file: String,
31    pub max_size: u32,
32    pub level_filter: RwLock<HashMap<String, log::LevelFilter>>,
33    pub fmt_cache: Mutex<VecDeque<Vec<u8>>>,
34    pub filter: Option<Box<dyn CustomFilter>>,
35    pub logger_data: Mutex<LogData>,
36    pub show_process_id: bool,
37    pub show_thread_info: bool,
38    pub show_module_path: bool,
39    pub highlight_keywords: Vec<String>,
40    pub quickwit_client: Option<QuickwitClient>,
41}
42
43impl LogPlus {
44    /// 创建新的日志器实例
45    pub fn new(
46        level: log::LevelFilter,
47        log_file: String,
48        max_size: u32,
49        filter: Option<Box<dyn CustomFilter>>,
50        logger_data: LogData,
51        show_process_id: bool,
52        show_thread_info: bool,
53        show_module_path: bool,
54        highlight_keywords: Vec<String>,
55        quickwit_client: Option<QuickwitClient>,
56    ) -> Self {
57        Self {
58            level,
59            log_file,
60            max_size,
61            level_filter: RwLock::new(HashMap::new()),
62            fmt_cache: Mutex::new(VecDeque::new()),
63            filter,
64            logger_data: Mutex::new(logger_data),
65            show_process_id,
66            show_thread_info,
67            show_module_path,
68            highlight_keywords,
69            quickwit_client,
70        }
71    }
72
73    /// 输出日志到控制台和文件
74    pub fn write(&self, msg: &[u8]) {
75        let mut logger_data = match self.logger_data.lock() {
76            Ok(v) => v,
77            Err(e) => {
78                eprint!("log mutex lock failed: {e:?}");
79                return;
80            }
81        };
82
83        // 如果启用了控制台输出,则写入控制台
84        if let Some(ref mut console) = logger_data.console {
85            console.write_all(msg).expect("write log to console fail");
86        }
87
88        // 判断日志长度是否到达最大限制,如果到了,需要备份当前日志文件并重新创建新的日志文件
89        if logger_data.log_size > self.max_size {
90            let mut log_file_closed = false;
91
92            // 如果启用了日志文件,刷新缓存并关闭日志文件
93            if let Some(ref mut fileout) = logger_data.fileout {
94                fileout.flush().expect("flush log file fail");
95                logger_data.fileout.take();
96                log_file_closed = true;
97            }
98
99            // 之所以把关闭文件和重新创建文件分开写,是因为rust限制了可变借用(fileout)只允许1次
100            if log_file_closed {
101                // 删除已有备份,并重命名现有文件为备份文件
102                let bak = format!("{}.bak", self.log_file);
103                std::fs::remove_file(&bak).unwrap_or_default();
104                std::fs::rename(&self.log_file, &bak).expect("backup log file fail");
105
106                match crate::utils::open_log_file_sync(&self.log_file) {
107                    Ok((writer, _)) => {
108                        logger_data.fileout = Some(writer);
109                    },
110                    Err(e) => {
111                        eprintln!("Failed to reopen log file {}: {}", self.log_file, e);
112                        return;
113                    }
114                }
115                logger_data.log_size = 0;
116            }
117        }
118
119        if let Some(ref mut fileout) = logger_data.fileout {
120            let ws = crate::utils::write_text(fileout, msg).unwrap();
121            logger_data.log_size += ws as u32;
122        }
123
124        if let Some(plugin) = &mut logger_data.plugin {
125            plugin.write_all(msg).expect("write log to plugin fail");
126        }
127    }
128
129    /// 刷新日志的控制台和文件缓存
130    pub fn flush_inner(&self) {
131        let mut logger_data = match self.logger_data.lock() {
132            Ok(v) => v,
133            Err(e) => {
134                eprint!("log mutex lock failed: {e:?}");
135                return;
136            }
137        };
138
139        if let Some(ref mut console) = logger_data.console {
140            if let Err(e) = crate::utils::safe_flush(console) {
141                eprintln!("Failed to flush console: {}", e);
142            }
143        }
144
145        if let Some(ref mut fileout) = logger_data.fileout {
146            if let Err(e) = crate::utils::safe_flush(fileout) {
147                eprintln!("Failed to flush log file: {}", e);
148            }
149        }
150    }
151
152    /// 应用关键词高亮
153    fn apply_keyword_highlighting(&self, message: &str) -> String {
154        if self.highlight_keywords.is_empty() {
155            return message.to_string();
156        }
157        
158        let mut result = message.to_string();
159        for keyword in &self.highlight_keywords {
160            if message.contains(keyword) {
161                let highlighted = format!("\x1b[41m{}\x1b[0m", keyword);
162                result = result.replace(keyword, &highlighted);
163            }
164        }
165        result
166    }
167}
168
169impl log::Log for LogPlus {
170    fn enabled(&self, metadata: &log::Metadata) -> bool {
171        if metadata.level() <= self.level {
172            if let Ok(level_filters) = self.level_filter.read() {
173                let mut target = metadata.target();
174                while !target.is_empty() {
175                    if let Some(level) = level_filters.get(target) {
176                        return metadata.level() <= *level;
177                    }
178
179                    target = match target.rfind("::") {
180                        Some(rpos) => &target[..rpos],
181                        None => ""
182                    };
183                }
184                return true;
185            }
186        }
187        false
188    }
189
190    fn log(&self, record: &log::Record) {
191        if !self.enabled(record.metadata()) { return; }
192        if let Some(filter) = &self.filter {
193            if !filter.enabled(record) { return; }
194        }
195
196        let now = std::time::SystemTime::now()
197            .duration_since(std::time::UNIX_EPOCH)
198            .unwrap()
199            .as_secs();
200        let now = format!("{}", now);
201
202        let mut msg = get_msg_from_cache();
203
204        // 日志条目格式化 - 优化后的格式
205        let is_detail = self.level >= log::LevelFilter::Debug;
206        let log_level = record.level();
207        
208        if is_detail {
209            // 详细模式:包含颜色、位置信息等
210            write!(&mut msg, "\x1b[90m{}\x1b[0m {}{:>5}\x1b[0m",
211                now, level_color(log_level), log_level
212            ).unwrap();
213            
214            // 根据配置添加进程ID
215            if self.show_process_id {
216                write!(&mut msg, " \x1b[90m[{}]\x1b[0m", std::process::id()).unwrap();
217            }
218            
219            // 根据配置添加线程信息
220            if self.show_thread_info {
221                if let Some(thread_name) = std::thread::current().name() {
222                    write!(&mut msg, " \x1b[90m[{}]\x1b[0m", thread_name).unwrap();
223                } else {
224                    write!(&mut msg, " \x1b[90m[{:?}]\x1b[0m", std::thread::current().id()).unwrap();
225                }
226            }
227            
228            // 根据配置添加模块和行号信息
229            if self.show_module_path {
230                let target = record.target();
231                let line = record.line().unwrap_or(0);
232                write!(&mut msg, " \x1b[90m{}:{}\x1b[0m", target, line).unwrap();
233            }
234            
235            // 消息内容 - 应用关键词高亮
236            let message = format!("{}", record.args());
237            let highlighted_message = self.apply_keyword_highlighting(&message);
238            write!(&mut msg, " ▶ {}\n", highlighted_message).unwrap();
239        } else {
240            // 简洁模式:只包含基本信息 - 应用关键词高亮
241            let message = format!("{}", record.args());
242            let highlighted_message = self.apply_keyword_highlighting(&message);
243            write!(&mut msg, "{} {:>5} ▶ {}\n", now, log_level, highlighted_message).unwrap();
244        }
245
246        // 异步写入模式
247        match self.logger_data.lock() {
248            Ok(logger_data) => {
249                if let Some(ref sender) = logger_data.sender {
250                    // 采用独立的单线程写入日志的方式,向channel发送要写入的日志消息即可
251                    sender.send(AsyncLogType::Message(msg)).unwrap();
252                    
253                    // 发送到 Quickwit(如果配置了)- 异步模式
254                    if let Some(ref _quickwit_client) = self.quickwit_client {
255                        let log_entry = QuickwitLogEntry {
256                            timestamp: std::time::SystemTime::now()
257                                .duration_since(std::time::UNIX_EPOCH)
258                                .unwrap()
259                                .as_secs(),
260                            level: record.level().to_string(),
261                            message: format!("{}", record.args()),
262                            module: record.module_path().map(|s| s.to_string()),
263                            file: record.file().map(|s| s.to_string()),
264                            line: record.line(),
265                            process_id: Some(std::process::id()),
266                            thread_id: std::thread::current().name().map(|s| s.to_string()),
267                            custom_fields: std::collections::HashMap::new(),
268                        };
269                        
270                        // 发送 Quickwit 日志到异步处理器
271                        if let Err(e) = sender.send(AsyncLogType::QuickwitLog(log_entry)) {
272                            eprintln!("✗ 发送 Quickwit 日志到异步处理器失败: {}", e);
273                        }
274                    }
275                    return;
276                }
277            },
278            Err(e) => {
279                eprint!("log mutex lock failed: {e:?}");
280                return;
281            }
282        }
283
284        // 同步写入模式
285        self.write(&msg);
286        put_msg_to_cache(msg.clone());
287        
288        // 发送到 Quickwit(如果配置了)
289          if let Some(ref quickwit_client) = self.quickwit_client {
290              let log_entry = QuickwitLogEntry {
291                  timestamp: std::time::SystemTime::now()
292                      .duration_since(std::time::UNIX_EPOCH)
293                      .unwrap()
294                      .as_secs(),
295                  level: record.level().to_string(),
296                  message: format!("{}", record.args()),
297                  module: record.module_path().map(|s| s.to_string()),
298                  file: record.file().map(|s| s.to_string()),
299                  line: record.line(),
300                  process_id: Some(std::process::id()),
301                  thread_id: std::thread::current().name().map(|s| s.to_string()),
302                  custom_fields: std::collections::HashMap::new(),
303              };
304              
305              // 同步发送到 Quickwit,避免生命周期问题
306              match quickwit_client.send_log(&log_entry) {
307                  Ok(()) => {
308                      // 可选:添加调试信息
309                      if std::env::var("QUICKWIT_DEBUG").is_ok() {
310                          eprintln!("✓ 日志已发送到 Quickwit: {}", log_entry.message);
311                      }
312                  },
313                  Err(e) => {
314                      eprintln!("✗ 发送日志到 Quickwit 失败: {}", e);
315                      eprintln!("  日志内容: {}", log_entry.message);
316                      eprintln!("  错误详情: {:?}", e);
317                  }
318              }
319          }
320    }
321
322    fn flush(&self) {
323        if let Ok(logger_data) = self.logger_data.lock() {
324            if let Some(ref sender) = logger_data.sender {
325                if let Err(e) = sender.send(AsyncLogType::Flush) {
326                    eprint!("failed in log::flush: {e:?}");
327                }
328            } else {
329                drop(logger_data);
330                self.flush_inner();
331            }
332        }
333    }
334}
335
336/// 为函数类型实现 CustomFilter trait
337impl<F: Fn(&log::Record) -> bool + Send + Sync + 'static> CustomFilter for F {
338    fn enabled(&self, record: &log::Record) -> bool {
339        self(record)
340    }
341}