open_lark/core/
error_logger.rs

1/// 错误日志记录模块
2///
3/// 提供结构化的错误日志记录功能:
4/// - 结构化日志输出
5/// - 多种日志格式
6/// - 日志级别控制
7/// - 上下文信息记录
8/// - 外部日志系统集成
9use std::collections::HashMap;
10use std::{fmt, time::SystemTime};
11
12use crate::core::{
13    error::LarkAPIError,
14    error_codes::LarkErrorCode,
15    error_helper::ErrorHandlingCategory,
16    error_metrics::{ErrorEvent, ErrorSeverity},
17};
18
19/// 日志级别
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21pub enum LogLevel {
22    /// 调试信息
23    Debug = 1,
24    /// 信息
25    Info = 2,
26    /// 警告
27    Warn = 3,
28    /// 错误
29    Error = 4,
30    /// 严重错误
31    Critical = 5,
32}
33
34impl LogLevel {
35    /// 从错误严重级别转换
36    pub fn from_error_severity(severity: ErrorSeverity) -> Self {
37        match severity {
38            ErrorSeverity::Info => Self::Info,
39            ErrorSeverity::Warning => Self::Warn,
40            ErrorSeverity::Error => Self::Error,
41            ErrorSeverity::Critical => Self::Critical,
42        }
43    }
44
45    /// 获取颜色代码(用于控制台输出)
46    pub fn color_code(&self) -> &'static str {
47        match self {
48            Self::Debug => "\x1b[36m",    // 青色
49            Self::Info => "\x1b[32m",     // 绿色
50            Self::Warn => "\x1b[33m",     // 黄色
51            Self::Error => "\x1b[31m",    // 红色
52            Self::Critical => "\x1b[35m", // 紫色
53        }
54    }
55
56    /// 重置颜色
57    pub fn reset_color() -> &'static str {
58        "\x1b[0m"
59    }
60
61    /// 获取显示标签
62    pub fn label(&self) -> &'static str {
63        match self {
64            Self::Debug => "DEBUG",
65            Self::Info => "INFO",
66            Self::Warn => "WARN",
67            Self::Error => "ERROR",
68            Self::Critical => "CRITICAL",
69        }
70    }
71}
72
73impl fmt::Display for LogLevel {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "{}", self.label())
76    }
77}
78
79/// 日志条目
80#[derive(Debug, Clone)]
81pub struct LogEntry {
82    /// 日志级别
83    pub level: LogLevel,
84    /// 时间戳
85    pub timestamp: SystemTime,
86    /// 消息
87    pub message: String,
88    /// 错误信息(如果有)
89    pub error: Option<LarkAPIError>,
90    /// 错误分类
91    pub category: Option<ErrorHandlingCategory>,
92    /// 错误码
93    pub error_code: Option<LarkErrorCode>,
94    /// 上下文信息
95    pub context: HashMap<String, String>,
96    /// 调用栈信息
97    pub caller: Option<String>,
98}
99
100impl LogEntry {
101    /// 创建新的日志条目
102    pub fn new(level: LogLevel, message: String) -> Self {
103        Self {
104            level,
105            timestamp: SystemTime::now(),
106            message,
107            error: None,
108            category: None,
109            error_code: None,
110            context: HashMap::new(),
111            caller: None,
112        }
113    }
114
115    /// 从错误事件创建日志条目
116    pub fn from_error_event(event: &ErrorEvent) -> Self {
117        let level = LogLevel::from_error_severity(event.severity_level());
118        let message = format!("API调用错误: {}", event.error);
119
120        Self {
121            level,
122            timestamp: event.timestamp,
123            message,
124            error: Some(event.error.clone()),
125            category: Some(event.category),
126            error_code: event.error_code,
127            context: event.context.clone(),
128            caller: None,
129        }
130    }
131
132    /// 添加上下文信息
133    pub fn with_context(mut self, key: &str, value: &str) -> Self {
134        self.context.insert(key.to_string(), value.to_string());
135        self
136    }
137
138    /// 添加调用者信息
139    pub fn with_caller(mut self, caller: String) -> Self {
140        self.caller = Some(caller);
141        self
142    }
143
144    /// 设置错误信息
145    pub fn with_error(mut self, error: LarkAPIError) -> Self {
146        self.error = Some(error);
147        self
148    }
149}
150
151/// 日志格式器
152pub trait LogFormatter {
153    /// 格式化日志条目
154    fn format(&self, entry: &LogEntry) -> String;
155}
156
157/// 简单文本格式器
158#[derive(Debug, Clone)]
159pub struct SimpleFormatter {
160    /// 是否包含时间戳
161    pub include_timestamp: bool,
162    /// 是否使用颜色
163    pub use_colors: bool,
164}
165
166impl Default for SimpleFormatter {
167    fn default() -> Self {
168        Self {
169            include_timestamp: true,
170            use_colors: true,
171        }
172    }
173}
174
175impl LogFormatter for SimpleFormatter {
176    fn format(&self, entry: &LogEntry) -> String {
177        let mut output = String::new();
178
179        // 添加颜色前缀
180        if self.use_colors {
181            output.push_str(entry.level.color_code());
182        }
183
184        // 添加时间戳
185        if self.include_timestamp {
186            if let Ok(duration) = entry.timestamp.duration_since(SystemTime::UNIX_EPOCH) {
187                let seconds = duration.as_secs();
188                let millis = duration.subsec_millis();
189                output.push_str(&format!("[{seconds}.{millis:03}] "));
190            }
191        }
192
193        // 添加日志级别
194        output.push_str(&format!("[{}] ", entry.level.label()));
195
196        // 添加消息
197        output.push_str(&entry.message);
198
199        // 添加错误信息
200        if let Some(ref error) = entry.error {
201            output.push_str(&format!(" | 错误: {error}"));
202        }
203
204        // 添加错误码
205        if let Some(code) = entry.error_code {
206            output.push_str(&format!(" | 错误码: {code}"));
207        }
208
209        // 添加上下文
210        if !entry.context.is_empty() {
211            output.push_str(" | 上下文: {");
212            for (i, (key, value)) in entry.context.iter().enumerate() {
213                if i > 0 {
214                    output.push_str(", ");
215                }
216                output.push_str(&format!("{key}={value}"));
217            }
218            output.push('}');
219        }
220
221        // 添加调用者信息
222        if let Some(ref caller) = entry.caller {
223            output.push_str(&format!(" | 调用者: {caller}"));
224        }
225
226        // 重置颜色
227        if self.use_colors {
228            output.push_str(LogLevel::reset_color());
229        }
230
231        output
232    }
233}
234
235/// JSON格式器
236#[derive(Debug, Clone)]
237pub struct JsonFormatter;
238
239impl LogFormatter for JsonFormatter {
240    fn format(&self, entry: &LogEntry) -> String {
241        use serde_json::{Map, Value};
242
243        let mut json_entry = Map::new();
244
245        // 基础字段
246        json_entry.insert(
247            "level".to_string(),
248            Value::String(entry.level.label().to_string()),
249        );
250        json_entry.insert(
251            "timestamp".to_string(),
252            Value::Number(serde_json::Number::from(
253                entry
254                    .timestamp
255                    .duration_since(SystemTime::UNIX_EPOCH)
256                    .unwrap_or_default()
257                    .as_millis() as u64,
258            )),
259        );
260        json_entry.insert("message".to_string(), Value::String(entry.message.clone()));
261
262        // 错误信息
263        if let Some(ref error) = entry.error {
264            json_entry.insert("error".to_string(), Value::String(error.to_string()));
265        }
266
267        // 错误分类
268        if let Some(category) = entry.category {
269            json_entry.insert(
270                "category".to_string(),
271                Value::String(format!("{category:?}")),
272            );
273        }
274
275        // 错误码
276        if let Some(code) = entry.error_code {
277            json_entry.insert(
278                "error_code".to_string(),
279                Value::Number(serde_json::Number::from(code as i32)),
280            );
281        }
282
283        // 上下文
284        if !entry.context.is_empty() {
285            let context_value = entry
286                .context
287                .iter()
288                .map(|(k, v)| (k.clone(), Value::String(v.clone())))
289                .collect::<Map<String, Value>>();
290            json_entry.insert("context".to_string(), Value::Object(context_value));
291        }
292
293        // 调用者
294        if let Some(ref caller) = entry.caller {
295            json_entry.insert("caller".to_string(), Value::String(caller.clone()));
296        }
297
298        serde_json::to_string(&Value::Object(json_entry)).unwrap_or_default()
299    }
300}
301
302/// 结构化格式器
303#[derive(Debug, Clone)]
304pub struct StructuredFormatter {
305    /// 字段分隔符
306    pub separator: String,
307    /// 键值分隔符
308    pub kv_separator: String,
309}
310
311impl Default for StructuredFormatter {
312    fn default() -> Self {
313        Self {
314            separator: " | ".to_string(),
315            kv_separator: "=".to_string(),
316        }
317    }
318}
319
320impl LogFormatter for StructuredFormatter {
321    fn format(&self, entry: &LogEntry) -> String {
322        let mut fields = Vec::new();
323
324        // 时间戳
325        if let Ok(duration) = entry.timestamp.duration_since(SystemTime::UNIX_EPOCH) {
326            fields.push(format!("time{}{}", self.kv_separator, duration.as_millis()));
327        }
328
329        // 级别
330        fields.push(format!("level{}{}", self.kv_separator, entry.level.label()));
331
332        // 消息
333        fields.push(format!("msg{}{}", self.kv_separator, entry.message));
334
335        // 错误信息
336        if let Some(ref error) = entry.error {
337            fields.push(format!("error{}{}", self.kv_separator, error));
338        }
339
340        // 错误分类
341        if let Some(category) = entry.category {
342            fields.push(format!("category{}{:?}", self.kv_separator, category));
343        }
344
345        // 错误码
346        if let Some(code) = entry.error_code {
347            fields.push(format!("error_code{}{}", self.kv_separator, code as i32));
348        }
349
350        // 上下文
351        for (key, value) in &entry.context {
352            fields.push(format!("{}{}{}", key, self.kv_separator, value));
353        }
354
355        // 调用者
356        if let Some(ref caller) = entry.caller {
357            fields.push(format!("caller{}{}", self.kv_separator, caller));
358        }
359
360        fields.join(&self.separator)
361    }
362}
363
364/// 日志记录器配置
365#[derive(Debug, Clone)]
366pub struct LoggerConfig {
367    /// 最小日志级别
368    pub min_level: LogLevel,
369    /// 格式器类型
370    pub formatter: FormatterType,
371    /// 输出目标
372    pub output: OutputTarget,
373    /// 是否记录上下文信息
374    pub include_context: bool,
375    /// 是否记录调用栈
376    pub include_caller: bool,
377}
378
379/// 格式器类型
380#[derive(Debug, Clone)]
381pub enum FormatterType {
382    Simple(SimpleFormatter),
383    Json(JsonFormatter),
384    Structured(StructuredFormatter),
385}
386
387/// 输出目标
388#[derive(Debug, Clone)]
389pub enum OutputTarget {
390    /// 标准输出
391    Stdout,
392    /// 标准错误
393    Stderr,
394    /// 文件
395    File(String),
396    /// 多个目标
397    Multiple(Vec<OutputTarget>),
398}
399
400impl Default for LoggerConfig {
401    fn default() -> Self {
402        Self {
403            min_level: LogLevel::Info,
404            formatter: FormatterType::Simple(SimpleFormatter::default()),
405            output: OutputTarget::Stdout,
406            include_context: true,
407            include_caller: false,
408        }
409    }
410}
411
412/// 错误日志记录器
413pub struct ErrorLogger {
414    config: LoggerConfig,
415}
416
417impl Default for ErrorLogger {
418    fn default() -> Self {
419        Self::new(LoggerConfig::default())
420    }
421}
422
423impl ErrorLogger {
424    /// 创建新的错误日志记录器
425    pub fn new(config: LoggerConfig) -> Self {
426        Self { config }
427    }
428
429    /// 记录日志条目
430    pub fn log(&self, entry: LogEntry) {
431        // 检查日志级别
432        if entry.level < self.config.min_level {
433            return;
434        }
435
436        // 格式化日志
437        let formatted = match &self.config.formatter {
438            FormatterType::Simple(formatter) => formatter.format(&entry),
439            FormatterType::Json(formatter) => formatter.format(&entry),
440            FormatterType::Structured(formatter) => formatter.format(&entry),
441        };
442
443        // 输出日志
444        self.output_log(&formatted);
445    }
446
447    /// 记录错误
448    pub fn error(&self, message: &str) {
449        let entry = LogEntry::new(LogLevel::Error, message.to_string());
450        self.log(entry);
451    }
452
453    /// 记录错误和上下文
454    pub fn error_with_context(&self, message: &str, context: HashMap<String, String>) {
455        let mut entry = LogEntry::new(LogLevel::Error, message.to_string());
456        entry.context = context;
457        self.log(entry);
458    }
459
460    /// 记录API错误
461    pub fn log_api_error(&self, error: &LarkAPIError) {
462        let event = ErrorEvent::from_error(error.clone());
463        let entry = LogEntry::from_error_event(&event);
464        self.log(entry);
465    }
466
467    /// 记录错误事件
468    pub fn log_error_event(&self, event: &ErrorEvent) {
469        let entry = LogEntry::from_error_event(event);
470        self.log(entry);
471    }
472
473    /// 记录警告
474    pub fn warn(&self, message: &str) {
475        let entry = LogEntry::new(LogLevel::Warn, message.to_string());
476        self.log(entry);
477    }
478
479    /// 记录信息
480    pub fn info(&self, message: &str) {
481        let entry = LogEntry::new(LogLevel::Info, message.to_string());
482        self.log(entry);
483    }
484
485    /// 记录调试信息
486    pub fn debug(&self, message: &str) {
487        let entry = LogEntry::new(LogLevel::Debug, message.to_string());
488        self.log(entry);
489    }
490
491    /// 输出日志到目标
492    fn output_log(&self, formatted: &str) {
493        match &self.config.output {
494            OutputTarget::Stdout => {
495                println!("{formatted}");
496            }
497            OutputTarget::Stderr => {
498                eprintln!("{formatted}");
499            }
500            OutputTarget::File(path) => {
501                self.write_to_file(path, formatted);
502            }
503            OutputTarget::Multiple(targets) => {
504                for target in targets {
505                    let temp_config = LoggerConfig {
506                        output: target.clone(),
507                        ..self.config.clone()
508                    };
509                    let temp_logger = ErrorLogger::new(temp_config);
510                    temp_logger.output_log(formatted);
511                }
512            }
513        }
514    }
515
516    /// 写入文件
517    fn write_to_file(&self, path: &str, content: &str) {
518        use std::{fs::OpenOptions, io::Write};
519
520        if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(path) {
521            let _ = writeln!(file, "{content}");
522        }
523    }
524}
525
526/// 日志记录器构建器
527pub struct LoggerBuilder {
528    config: LoggerConfig,
529}
530
531impl LoggerBuilder {
532    /// 创建新的构建器
533    pub fn new() -> Self {
534        Self {
535            config: LoggerConfig::default(),
536        }
537    }
538
539    /// 设置最小日志级别
540    pub fn min_level(mut self, level: LogLevel) -> Self {
541        self.config.min_level = level;
542        self
543    }
544
545    /// 使用简单格式器
546    pub fn simple_format(mut self) -> Self {
547        self.config.formatter = FormatterType::Simple(SimpleFormatter::default());
548        self
549    }
550
551    /// 使用JSON格式器
552    pub fn json_format(mut self) -> Self {
553        self.config.formatter = FormatterType::Json(JsonFormatter);
554        self
555    }
556
557    /// 使用结构化格式器
558    pub fn structured_format(mut self) -> Self {
559        self.config.formatter = FormatterType::Structured(StructuredFormatter::default());
560        self
561    }
562
563    /// 输出到文件
564    pub fn output_to_file(mut self, path: &str) -> Self {
565        self.config.output = OutputTarget::File(path.to_string());
566        self
567    }
568
569    /// 输出到标准错误
570    pub fn output_to_stderr(mut self) -> Self {
571        self.config.output = OutputTarget::Stderr;
572        self
573    }
574
575    /// 包含上下文信息
576    pub fn include_context(mut self, include: bool) -> Self {
577        self.config.include_context = include;
578        self
579    }
580
581    /// 构建日志记录器
582    pub fn build(self) -> ErrorLogger {
583        ErrorLogger::new(self.config)
584    }
585}
586
587impl Default for LoggerBuilder {
588    fn default() -> Self {
589        Self::new()
590    }
591}
592
593#[cfg(test)]
594mod tests {
595    use super::*;
596
597    #[test]
598    fn test_log_level_ordering() {
599        assert!(LogLevel::Debug < LogLevel::Info);
600        assert!(LogLevel::Error > LogLevel::Warn);
601        assert!(LogLevel::Critical > LogLevel::Error);
602    }
603
604    #[test]
605    fn test_log_entry_creation() {
606        let entry = LogEntry::new(LogLevel::Error, "Test message".to_string());
607        assert_eq!(entry.level, LogLevel::Error);
608        assert_eq!(entry.message, "Test message");
609        assert!(entry.context.is_empty());
610    }
611
612    #[test]
613    fn test_simple_formatter() {
614        let formatter = SimpleFormatter::default();
615        let entry = LogEntry::new(LogLevel::Info, "Test message".to_string());
616        let formatted = formatter.format(&entry);
617
618        assert!(formatted.contains("[INFO]"));
619        assert!(formatted.contains("Test message"));
620    }
621
622    #[test]
623    fn test_json_formatter() {
624        let formatter = JsonFormatter;
625        let entry = LogEntry::new(LogLevel::Error, "Error message".to_string());
626        let formatted = formatter.format(&entry);
627
628        assert!(formatted.contains("\"level\":\"ERROR\""));
629        assert!(formatted.contains("\"message\":\"Error message\""));
630    }
631
632    #[test]
633    fn test_structured_formatter() {
634        let formatter = StructuredFormatter::default();
635        let entry = LogEntry::new(LogLevel::Warn, "Warning message".to_string());
636        let formatted = formatter.format(&entry);
637
638        assert!(formatted.contains("level=WARN"));
639        assert!(formatted.contains("msg=Warning message"));
640    }
641
642    #[test]
643    fn test_logger_builder() {
644        let logger = LoggerBuilder::new()
645            .min_level(LogLevel::Debug)
646            .json_format()
647            .output_to_stderr()
648            .build();
649
650        assert_eq!(logger.config.min_level, LogLevel::Debug);
651        matches!(logger.config.formatter, FormatterType::Json(_));
652        matches!(logger.config.output, OutputTarget::Stderr);
653    }
654}