Skip to main content

rusty_rich/
logging.rs

1//! Logging integration — equivalent to Rich's `logging.py`.
2//!
3//! Provides a log handler that renders log records with Rich formatting.
4
5use std::io::Write;
6
7use crate::console::Console;
8use crate::highlighter::ReprHighlighter;
9use crate::style::Style;
10use crate::text::Text;
11
12/// A handler that renders Python-style log records with Rich formatting.
13///
14/// In Rust, this integrates with the `log` crate.
15pub struct RichHandler {
16    pub console: Console,
17    pub show_time: bool,
18    pub show_level: bool,
19    pub show_path: bool,
20    pub enable_link_path: bool,
21    pub markup: bool,
22    pub highlighter: ReprHighlighter,
23}
24
25impl RichHandler {
26    /// Create a new `RichHandler` with default settings (shows time, level, and path).
27    pub fn new() -> Self {
28        Self {
29            console: Console::new(),
30            show_time: true,
31            show_level: true,
32            show_path: true,
33            enable_link_path: false,
34            markup: false,
35            highlighter: ReprHighlighter::new(),
36        }
37    }
38
39    /// Render a single log record.
40    pub fn render(
41        &self,
42        level: log::Level,
43        message: &str,
44        _module_path: Option<&str>,
45        file: Option<&str>,
46        line: Option<u32>,
47    ) -> String {
48        let mut text = Text::new("");
49
50        if self.show_time {
51            let now = chrono::Local::now();
52            let time_str = format!("[{}]", now.format("%H:%M:%S"));
53            text.append_styled(time_str, Style::new().dim(true));
54            text.append_styled(" ", Style::new());
55        }
56
57        if self.show_level {
58            let level_style = match level {
59                log::Level::Error => Style::new().color(crate::color::Color::parse("red").unwrap()).bold(true),
60                log::Level::Warn => Style::new().color(crate::color::Color::parse("yellow").unwrap()),
61                log::Level::Info => Style::new().color(crate::color::Color::parse("green").unwrap()),
62                log::Level::Debug => Style::new().color(crate::color::Color::parse("blue").unwrap()),
63                log::Level::Trace => Style::new().color(crate::color::Color::parse("bright_black").unwrap()),
64            };
65            text.append_styled(format!("{level:<5}"), level_style);
66            text.append_styled(" ", Style::new());
67        }
68
69        // Message
70        text.append_styled(message, Style::new());
71
72        // File location
73        if self.show_path {
74            if let (Some(file), Some(line)) = (file, line) {
75                text.append_styled(" ", Style::new());
76                let location = format!("{file}:{line}");
77                text.append_styled(format!("[{location}]"), Style::new().dim(true).italic(true));
78            }
79        }
80
81        text.render()
82    }
83
84    /// Emit a `log` crate [`Record`](log::Record) using Rich formatting.
85    ///
86    /// Formats the record and writes it to the console.
87    pub fn emit(&mut self, record: &log::Record) {
88        let formatted = self.render(
89            record.level(),
90            record.args().to_string().as_str(),
91            record.module_path(),
92            record.file(),
93            record.line(),
94        );
95        let _ = writeln!(self.console.file, "{formatted}");
96        let _ = self.console.file.flush();
97    }
98}
99
100impl Default for RichHandler {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106/// Convenience: install a Rich logger.
107pub fn install() -> Result<(), log::SetLoggerError> {
108    // This requires a static/global logger; for simplicity, we'll just
109    // provide the handler for manual use.
110    Ok(())
111}
112
113/// Style a log level name.
114pub fn style_level(level: log::Level) -> Style {
115    match level {
116        log::Level::Error => Style::new()
117            .color(crate::color::Color::parse("red").unwrap())
118            .bold(true),
119        log::Level::Warn => Style::new()
120            .color(crate::color::Color::parse("yellow").unwrap()),
121        log::Level::Info => Style::new()
122            .color(crate::color::Color::parse("green").unwrap()),
123        log::Level::Debug => Style::new()
124            .color(crate::color::Color::parse("blue").unwrap()),
125        log::Level::Trace => Style::new()
126            .color(crate::color::Color::parse("bright_black").unwrap()),
127    }
128}