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