clang_log/
lib.rs

1#![forbid(missing_docs)]
2
3//! I really like the logging format of `clang` (pronounced klang, not C-lang), so I recreated it as an implementation of the `log` crate. It's licensed under the MIT license, and you can use it as you like. Use it, fork it, steal the code and sell it, I don't really care. If you find bugs, first of all, good job, because how can a logging implementation have bugs, and second of all, you can submit them to the issue tracker, or fix them and submit a PR. Additionally, if you want to improve `clang_log`, go ahead and waste five minuites writing a PR to change something.
4//!
5//! ## How to use
6//!
7//! To use `clang_log`, first include it, and `log` in `Cargo.toml`:
8//!
9//! ```toml
10//! [dependencies]
11//! log = "0.4.27"
12//! clang_log = "2.1.2"
13//! ```
14//!
15//! `clang_log` also has a feature flag; `color`. By removing this feature flag, you can remove color from the output.
16//!
17//! ```toml
18//! [dependencies]
19//! log = "0.4.27"
20//! clang_log = { version = "2.1.2", default-features = false }
21//! ```
22//!
23//! After adding `clang_log` to `Cargo.toml`, initialize it at the start of the program:
24//! ```rust
25//! # #[allow(clippy::needless_doctest_main)]
26//! use log::Level;
27//!
28//! fn main() {
29//!     clang_log::init(Level::Trace, "clang")
30//! }
31//! ```
32//!
33//! You can also specify a minimum level to start outputting to stderr:
34//! ```rust
35//! # #[allow(clippy::needless_doctest_main)]
36//! use log::Level;
37//!
38//! fn main() {
39//!     clang_log::init_error(Level::Trace, Level::Warn, "clang")
40//! }
41//! ```
42//!
43//! To use it, just use the macros provided by `log`
44//!
45//! ```rust
46//! use log::*;
47//!
48//! error!("Could not find files!");
49//! ```
50
51use std::io::{Write, stdout};
52
53use log::*;
54
55#[cfg(feature = "color")]
56use colored::Colorize;
57
58#[cfg(not(feature = "color"))]
59// NOP implementation for Colorize
60trait Colorize: Sized {
61    fn bold(self) -> Self {
62        self
63    }
64    fn white(self) -> Self {
65        self
66    }
67    fn red(self) -> Self {
68        self
69    }
70    fn bright_purple(self) -> Self {
71        self
72    }
73    fn bright_black(self) -> Self {
74        self
75    }
76    fn yellow(self) -> Self {
77        self
78    }
79}
80#[cfg(not(feature = "color"))]
81impl Colorize for &'static str {}
82
83/// Initialize logger with fields
84/// # Example
85/// ```rust
86/// use log::*;
87///
88/// clang_log::init(Level::Trace, "clang");
89/// ```
90pub fn init(min_level: Level, prog_name: &str) {
91    set_max_level(min_level.to_level_filter());
92
93    let logger = Logger {
94        min_level,
95        min_error_level: Level::Error,
96        prog_name: String::from(prog_name),
97        newline_sep: format!("\n{} ", "    | ".white().bold()),
98    };
99    if set_boxed_logger(Box::new(logger)).is_err() {
100        debug!("Logger initialized twice");
101    }
102}
103
104/// Initialize logger with fields.
105/// # Example
106/// ```rust
107/// use log::*;
108///
109/// clang_log::init_error(Level::Trace, Level::Warn, "clang");
110/// ```
111pub fn init_error(min_level: Level, min_error_level: Level, prog_name: &str) {
112    set_max_level(min_level.to_level_filter());
113
114    let logger = Logger {
115        min_level,
116        min_error_level,
117        prog_name: String::from(prog_name),
118        newline_sep: format!("\n{} ", "    | ".white().bold()),
119    };
120    if set_boxed_logger(Box::new(logger)).is_err() {
121        debug!("Logger initialized twice");
122    }
123}
124
125/// Logger struct. Use `init()` instead, since `Logger` is useless without it being set as the logger, and `init()` does that anyway.
126pub struct Logger {
127    /// Minimum level this logger will print. For example: `Level::Trace`
128    pub min_level: Level,
129    /// Minimum level this logger will print to stderr. For example: `Level::Warn`
130    pub min_error_level: Level,
131    /// Name of the program, set to "clang" in clang. (If clang used clang_log)
132    pub prog_name: String,
133    /// Constant newline separator, inserted between every newline. Avoids many allocations by storing this as a field.
134    pub newline_sep: String,
135}
136
137impl Log for Logger {
138    fn enabled(&self, metadata: &Metadata) -> bool {
139        metadata.level() <= self.min_level
140    }
141
142    fn log(&self, record: &Record) {
143        if !self.enabled(record.metadata()) {
144            return;
145        }
146
147        let msg = format!(
148            "{}: {} {}",
149            self.prog_name,
150            match record.level() {
151                Level::Error => {
152                    "error:".red().bold()
153                }
154                Level::Warn => {
155                    "warning:".bright_purple().bold()
156                }
157                Level::Info => {
158                    "info:".bright_black().bold()
159                    //"note".black().bold() // Clang Behavior
160                }
161                Level::Debug => {
162                    "debug:".yellow().bold() // Clang doesn't have debug logs
163                }
164                Level::Trace => {
165                    "trace:".white().bold() // Clang doesn't have trace logs
166                }
167            },
168            record.args().to_string().replace('\n', &self.newline_sep)
169        );
170
171        if record.level() >= self.min_error_level {
172            eprintln!("{msg}");
173        } else {
174            println!("{msg}");
175        }
176    }
177
178    fn flush(&self) {
179        stdout().flush().expect("could not flush to console"); // Just in case
180    }
181}