fstdout_logger/formatter/
mod.rs

1//! Log message formatting functionality.
2//!
3//! This module is responsible for formatting log messages for display in the terminal
4//! and for writing to log files. It handles colored output, timestamp formatting,
5//! and determining which information to include in log messages.
6
7use colored::{ColoredString, Colorize};
8use log::{Level, Record};
9
10use crate::config::LoggerConfig;
11
12/// Handles log formatting for both stdout and file outputs.
13///
14/// This struct is responsible for:
15/// - Formatting log messages differently for stdout and file outputs
16/// - Applying colors to terminal output when enabled
17/// - Including/excluding file information based on configuration
18/// - Managing date/time formatting in log messages
19pub struct LogFormatter {
20    /// The configuration that controls formatting behavior
21    config: LoggerConfig,
22}
23
24impl LogFormatter {
25    /// Create a new formatter with the given configuration.
26    ///
27    /// # Arguments
28    ///
29    /// * `config` - Configuration options that control formatting behavior
30    pub fn new(config: LoggerConfig) -> Self {
31        Self { config }
32    }
33
34    /// Get the appropriate color for a log level.
35    ///
36    /// Returns a `ColoredString` with the appropriate color for the given log level,
37    /// or a plain string if colors are disabled.
38    ///
39    /// # Colors Used
40    ///
41    /// - `Error`: Bold Red
42    /// - `Warn`: Bold Yellow
43    /// - `Info`: Bold Blue
44    /// - `Debug`: Green
45    /// - `Trace`: Normal terminal color
46    ///
47    /// # Arguments
48    ///
49    /// * `level` - The log level to get the color for
50    fn get_level_color(&self, level: Level) -> ColoredString {
51        if !self.config.use_colors {
52            return level.as_str().normal();
53        }
54
55        match level {
56            Level::Error => "ERROR".red().bold(),
57            Level::Warn => "WARN".yellow().bold(),
58            Level::Info => "INFO".blue().bold(),
59            Level::Debug => "DEBUG".green(),
60            Level::Trace => "TRACE".normal(),
61        }
62    }
63
64    /// Format a log record for stdout
65    pub fn format_stdout(&self, record: &Record) -> String {
66        let now = chrono::Local::now();
67
68        // Format timestamp (HH:MM:SS) without date for stdout
69        let timestamp = if self.config.show_date_in_stdout {
70            now.format("%Y-%m-%d %H:%M:%S").to_string()
71        } else {
72            now.format("%H:%M:%S").to_string()
73        };
74
75        // Get colored log level
76        let level_str = self.get_level_color(record.level());
77
78        // Format with or without file info
79        if self.config.show_file_info {
80            let file = record.file().unwrap_or("unknown");
81            let line = record.line().unwrap_or(0);
82
83            if self.config.use_colors {
84                let file_info = format!("{file}:{line}").bright_black();
85                format!(
86                    "[{} {} {}] {}",
87                    timestamp.bright_black(),
88                    level_str,
89                    file_info,
90                    record.args()
91                )
92            } else {
93                format!(
94                    "[{} {} {}:{}] {}",
95                    timestamp,
96                    level_str,
97                    file,
98                    line,
99                    record.args()
100                )
101            }
102        } else {
103            // Simpler format without file info
104            if self.config.use_colors {
105                format!(
106                    "[{} {}] {}",
107                    timestamp.bright_black(),
108                    level_str,
109                    record.args()
110                )
111            } else {
112                format!("[{} {}] {}", timestamp, level_str, record.args())
113            }
114        }
115    }
116
117    /// Format a log record for file output.
118    ///
119    /// This creates a formatted log message for writing to a log file.
120    /// It always includes:
121    /// - Full date and time (YYYY-MM-DD HH:MM:SS)
122    /// - File and line information
123    /// - Plain text (no color codes)
124    ///
125    /// # Format
126    ///
127    /// `[YYYY-MM-DD HH:MM:SS LEVEL file:line] message\n`
128    ///
129    /// # Arguments
130    ///
131    /// * `record` - The log record to format
132    ///
133    /// # Returns
134    ///
135    /// A formatted string ready for writing to a file (includes trailing newline)
136    pub fn format_file(&self, record: &Record) -> String {
137        let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
138        let file = record.file().unwrap_or("unknown");
139        let line = record.line().unwrap_or(0);
140
141        format!(
142            "[{} {} {}:{}] {}\n",
143            timestamp,
144            record.level(),
145            file,
146            line,
147            record.args()
148        )
149    }
150}