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    use rstest::rstest;
597    use std::collections::HashMap;
598
599    #[test]
600    fn test_log_level_ordering() {
601        assert!(LogLevel::Debug < LogLevel::Info);
602        assert!(LogLevel::Error > LogLevel::Warn);
603        assert!(LogLevel::Critical > LogLevel::Error);
604    }
605
606    #[test]
607    fn test_log_entry_creation() {
608        let entry = LogEntry::new(LogLevel::Error, "Test message".to_string());
609        assert_eq!(entry.level, LogLevel::Error);
610        assert_eq!(entry.message, "Test message");
611        assert!(entry.context.is_empty());
612    }
613
614    #[test]
615    fn test_simple_formatter() {
616        let formatter = SimpleFormatter::default();
617        let entry = LogEntry::new(LogLevel::Info, "Test message".to_string());
618        let formatted = formatter.format(&entry);
619
620        assert!(formatted.contains("[INFO]"));
621        assert!(formatted.contains("Test message"));
622    }
623
624    #[test]
625    fn test_json_formatter() {
626        let formatter = JsonFormatter;
627        let entry = LogEntry::new(LogLevel::Error, "Error message".to_string());
628        let formatted = formatter.format(&entry);
629
630        assert!(formatted.contains("\"level\":\"ERROR\""));
631        assert!(formatted.contains("\"message\":\"Error message\""));
632    }
633
634    #[test]
635    fn test_structured_formatter() {
636        let formatter = StructuredFormatter::default();
637        let entry = LogEntry::new(LogLevel::Warn, "Warning message".to_string());
638        let formatted = formatter.format(&entry);
639
640        assert!(formatted.contains("level=WARN"));
641        assert!(formatted.contains("msg=Warning message"));
642    }
643
644    #[test]
645    fn test_logger_builder() {
646        let logger = LoggerBuilder::new()
647            .min_level(LogLevel::Debug)
648            .json_format()
649            .output_to_stderr()
650            .build();
651
652        assert_eq!(logger.config.min_level, LogLevel::Debug);
653        matches!(logger.config.formatter, FormatterType::Json(_));
654        matches!(logger.config.output, OutputTarget::Stderr);
655    }
656
657    // New comprehensive tests
658
659    #[rstest]
660    #[case(ErrorSeverity::Info, LogLevel::Info)]
661    #[case(ErrorSeverity::Warning, LogLevel::Warn)]
662    #[case(ErrorSeverity::Error, LogLevel::Error)]
663    #[case(ErrorSeverity::Critical, LogLevel::Critical)]
664    fn test_log_level_from_error_severity(
665        #[case] severity: ErrorSeverity,
666        #[case] expected_level: LogLevel,
667    ) {
668        assert_eq!(LogLevel::from_error_severity(severity), expected_level);
669    }
670
671    #[test]
672    fn test_log_level_color_codes() {
673        assert_eq!(LogLevel::Debug.color_code(), "\x1b[36m");
674        assert_eq!(LogLevel::Info.color_code(), "\x1b[32m");
675        assert_eq!(LogLevel::Warn.color_code(), "\x1b[33m");
676        assert_eq!(LogLevel::Error.color_code(), "\x1b[31m");
677        assert_eq!(LogLevel::Critical.color_code(), "\x1b[35m");
678        assert_eq!(LogLevel::reset_color(), "\x1b[0m");
679    }
680
681    #[test]
682    fn test_log_level_labels() {
683        assert_eq!(LogLevel::Debug.label(), "DEBUG");
684        assert_eq!(LogLevel::Info.label(), "INFO");
685        assert_eq!(LogLevel::Warn.label(), "WARN");
686        assert_eq!(LogLevel::Error.label(), "ERROR");
687        assert_eq!(LogLevel::Critical.label(), "CRITICAL");
688    }
689
690    #[test]
691    fn test_log_level_display() {
692        assert_eq!(format!("{}", LogLevel::Debug), "DEBUG");
693        assert_eq!(format!("{}", LogLevel::Info), "INFO");
694    }
695
696    #[test]
697    fn test_log_entry_with_context() {
698        let entry = LogEntry::new(LogLevel::Error, "Test message".to_string())
699            .with_context("key1", "value1")
700            .with_context("key2", "value2");
701
702        assert_eq!(entry.context.len(), 2);
703        assert_eq!(entry.context.get("key1"), Some(&"value1".to_string()));
704        assert_eq!(entry.context.get("key2"), Some(&"value2".to_string()));
705    }
706
707    #[test]
708    fn test_log_entry_with_caller() {
709        let entry = LogEntry::new(LogLevel::Info, "Test message".to_string())
710            .with_caller("test_function".to_string());
711
712        assert_eq!(entry.caller, Some("test_function".to_string()));
713    }
714
715    #[test]
716    fn test_log_entry_with_error() {
717        let error = LarkAPIError::MissingAccessToken;
718        let entry =
719            LogEntry::new(LogLevel::Error, "Test message".to_string()).with_error(error.clone());
720
721        assert!(entry.error.is_some());
722        match entry.error.unwrap() {
723            LarkAPIError::MissingAccessToken => (),
724            _ => panic!("Wrong error type"),
725        }
726    }
727
728    #[test]
729    fn test_log_entry_from_error_event() {
730        let error = LarkAPIError::api_error(403, "Forbidden", None);
731        let event = ErrorEvent::from_error(error.clone());
732        let entry = LogEntry::from_error_event(&event);
733
734        assert_eq!(entry.level, LogLevel::Error);
735        assert!(entry.message.contains("API调用错误"));
736        assert!(entry.error.is_some());
737        assert!(entry.category.is_some());
738    }
739
740    #[test]
741    fn test_simple_formatter_with_options() {
742        // Test without timestamp
743        let formatter = SimpleFormatter {
744            include_timestamp: false,
745            ..Default::default()
746        };
747        let entry = LogEntry::new(LogLevel::Info, "Test".to_string());
748        let formatted = formatter.format(&entry);
749        // Should still contain [INFO] but not timestamp
750        assert!(formatted.contains("[INFO]"));
751        assert!(!formatted.contains(".")); // No milliseconds timestamp
752
753        // Test without colors
754        let formatter = SimpleFormatter {
755            use_colors: false,
756            ..Default::default()
757        };
758        let formatted = formatter.format(&entry);
759        assert!(!formatted.contains("\x1b["));
760    }
761
762    #[test]
763    fn test_simple_formatter_with_complete_entry() {
764        let formatter = SimpleFormatter::default();
765        let error = LarkAPIError::api_error(400, "Bad Request", None);
766        let mut entry = LogEntry::new(LogLevel::Error, "API call failed".to_string())
767            .with_error(error)
768            .with_context("endpoint", "/api/test")
769            .with_caller("test_function".to_string());
770        entry.error_code = Some(LarkErrorCode::BadRequest);
771
772        let formatted = formatter.format(&entry);
773
774        assert!(formatted.contains("[ERROR]"));
775        assert!(formatted.contains("API call failed"));
776        assert!(formatted.contains("错误:"));
777        assert!(formatted.contains("错误码:"));
778        assert!(formatted.contains("上下文:"));
779        assert!(formatted.contains("调用者:"));
780    }
781
782    #[test]
783    fn test_json_formatter_complete() {
784        let formatter = JsonFormatter;
785        let error = LarkAPIError::IllegalParamError("invalid param".to_string());
786        let mut entry = LogEntry::new(LogLevel::Error, "Validation failed".to_string())
787            .with_error(error)
788            .with_context("field", "username")
789            .with_caller("validate_user".to_string());
790        entry.category = Some(ErrorHandlingCategory::ClientError);
791        entry.error_code = Some(LarkErrorCode::BadRequest);
792
793        let formatted = formatter.format(&entry);
794
795        assert!(formatted.contains("\"level\":\"ERROR\""));
796        assert!(formatted.contains("\"message\":\"Validation failed\""));
797        assert!(formatted.contains("\"error\":"));
798        assert!(formatted.contains("\"category\":\"ClientError\""));
799        assert!(formatted.contains("\"error_code\":400"));
800        assert!(formatted.contains("\"context\":{"));
801        assert!(formatted.contains("\"caller\":\"validate_user\""));
802    }
803
804    #[test]
805    fn test_structured_formatter_custom() {
806        let formatter = StructuredFormatter {
807            separator: " || ".to_string(),
808            kv_separator: ":".to_string(),
809        };
810
811        let entry = LogEntry::new(LogLevel::Warn, "Warning message".to_string())
812            .with_context("module", "auth");
813
814        let formatted = formatter.format(&entry);
815
816        assert!(formatted.contains("level:WARN"));
817        assert!(formatted.contains("msg:Warning message"));
818        assert!(formatted.contains("module:auth"));
819        assert!(formatted.contains(" || "));
820    }
821
822    #[test]
823    fn test_logger_config_default() {
824        let config = LoggerConfig::default();
825
826        assert_eq!(config.min_level, LogLevel::Info);
827        assert!(matches!(config.formatter, FormatterType::Simple(_)));
828        assert!(matches!(config.output, OutputTarget::Stdout));
829        assert!(config.include_context);
830        assert!(!config.include_caller);
831    }
832
833    #[test]
834    fn test_error_logger_default() {
835        let logger = ErrorLogger::default();
836        assert_eq!(logger.config.min_level, LogLevel::Info);
837    }
838
839    #[test]
840    fn test_error_logger_new() {
841        let config = LoggerConfig {
842            min_level: LogLevel::Debug,
843            formatter: FormatterType::Json(JsonFormatter),
844            output: OutputTarget::Stderr,
845            include_context: false,
846            include_caller: true,
847        };
848        let logger = ErrorLogger::new(config);
849        assert_eq!(logger.config.min_level, LogLevel::Debug);
850    }
851
852    #[test]
853    fn test_error_logger_log_level_filtering() {
854        let config = LoggerConfig {
855            min_level: LogLevel::Warn,
856            ..LoggerConfig::default()
857        };
858        let logger = ErrorLogger::new(config);
859
860        // This would normally print, but we can't easily test stdout
861        // Instead we test the logic by checking the min_level
862        assert_eq!(logger.config.min_level, LogLevel::Warn);
863
864        // Debug and Info should be filtered out
865        assert!(LogLevel::Debug < logger.config.min_level);
866        assert!(LogLevel::Info < logger.config.min_level);
867
868        // Warn, Error, Critical should pass
869        assert!(LogLevel::Warn >= logger.config.min_level);
870        assert!(LogLevel::Error >= logger.config.min_level);
871        assert!(LogLevel::Critical >= logger.config.min_level);
872    }
873
874    #[test]
875    fn test_error_logger_convenience_methods() {
876        let logger = ErrorLogger::default();
877
878        // These methods should not panic
879        logger.error("Test error");
880        logger.warn("Test warning");
881        logger.info("Test info");
882        logger.debug("Test debug");
883    }
884
885    #[test]
886    fn test_error_logger_error_with_context() {
887        let logger = ErrorLogger::default();
888        let mut context = HashMap::new();
889        context.insert("user_id".to_string(), "123".to_string());
890        context.insert("action".to_string(), "login".to_string());
891
892        // Should not panic
893        logger.error_with_context("Login failed", context);
894    }
895
896    #[test]
897    fn test_error_logger_log_api_error() {
898        let logger = ErrorLogger::default();
899        let error = LarkAPIError::api_error(429, "Too Many Requests", None);
900
901        // Should not panic
902        logger.log_api_error(&error);
903    }
904
905    #[test]
906    fn test_error_logger_log_error_event() {
907        let logger = ErrorLogger::default();
908        let error = LarkAPIError::RequestError("Network timeout".to_string());
909        let event = ErrorEvent::from_error(error);
910
911        // Should not panic
912        logger.log_error_event(&event);
913    }
914
915    #[test]
916    fn test_logger_builder_complete() {
917        let logger = LoggerBuilder::new()
918            .min_level(LogLevel::Critical)
919            .simple_format()
920            .output_to_file("/tmp/test.log")
921            .include_context(false)
922            .build();
923
924        assert_eq!(logger.config.min_level, LogLevel::Critical);
925        assert!(matches!(logger.config.formatter, FormatterType::Simple(_)));
926        assert!(matches!(logger.config.output, OutputTarget::File(_)));
927        assert!(!logger.config.include_context);
928    }
929
930    #[test]
931    fn test_logger_builder_structured_format() {
932        let logger = LoggerBuilder::new().structured_format().build();
933
934        assert!(matches!(
935            logger.config.formatter,
936            FormatterType::Structured(_)
937        ));
938    }
939
940    #[test]
941    fn test_logger_builder_default() {
942        let builder = LoggerBuilder::default();
943        let logger = builder.build();
944        assert_eq!(logger.config.min_level, LogLevel::Info);
945    }
946
947    #[test]
948    fn test_output_target_multiple() {
949        let targets = vec![
950            OutputTarget::Stdout,
951            OutputTarget::Stderr,
952            OutputTarget::File("/tmp/test.log".to_string()),
953        ];
954        let config = LoggerConfig {
955            output: OutputTarget::Multiple(targets),
956            ..LoggerConfig::default()
957        };
958        let logger = ErrorLogger::new(config);
959
960        // Should not panic when logging to multiple targets
961        logger.info("Test message for multiple targets");
962    }
963
964    #[test]
965    fn test_formatter_type_variants() {
966        let simple = FormatterType::Simple(SimpleFormatter::default());
967        let json = FormatterType::Json(JsonFormatter);
968        let structured = FormatterType::Structured(StructuredFormatter::default());
969
970        // Test that the variants exist and can be matched
971        match simple {
972            FormatterType::Simple(_) => (),
973            _ => panic!("Wrong formatter type"),
974        }
975
976        match json {
977            FormatterType::Json(_) => (),
978            _ => panic!("Wrong formatter type"),
979        }
980
981        match structured {
982            FormatterType::Structured(_) => (),
983            _ => panic!("Wrong formatter type"),
984        }
985    }
986
987    #[test]
988    fn test_log_entry_timestamp() {
989        let entry = LogEntry::new(LogLevel::Info, "Test".to_string());
990        let now = SystemTime::now();
991
992        // Timestamp should be recent (within 1 second)
993        let diff = now.duration_since(entry.timestamp).unwrap_or_default();
994        assert!(diff.as_secs() < 1);
995    }
996}