#![forbid(missing_docs)]
//! 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.
//!
//! ## How to use
//!
//! To use `clang_log`, first include it, and `log` in `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! log = "0.4.27"
//! clang_log = "2.1.2"
//! ```
//!
//! `clang_log` also has a feature flag; `color`. By removing this feature flag, you can remove color from the output.
//!
//! ```toml
//! [dependencies]
//! log = "0.4.27"
//! clang_log = { version = "2.1.2", default-features = false }
//! ```
//!
//! After adding `clang_log` to `Cargo.toml`, initialize it at the start of the program:
//! ```rust
//! # #[allow(clippy::needless_doctest_main)]
//! use log::Level;
//!
//! fn main() {
//! clang_log::init(Level::Trace, "clang")
//! }
//! ```
//!
//! You can also specify a minimum level to start outputting to stderr:
//! ```rust
//! # #[allow(clippy::needless_doctest_main)]
//! use log::Level;
//!
//! fn main() {
//! clang_log::init_error(Level::Trace, Level::Warn, "clang")
//! }
//! ```
//!
//! To use it, just use the macros provided by `log`
//!
//! ```rust
//! use log::*;
//!
//! error!("Could not find files!");
//! ```
use std::io::{Write, stdout};
use log::*;
#[cfg(feature = "color")]
use colored::Colorize;
#[cfg(not(feature = "color"))]
// NOP implementation for Colorize
trait Colorize: Sized {
fn bold(self) -> Self {
self
}
fn white(self) -> Self {
self
}
fn red(self) -> Self {
self
}
fn bright_purple(self) -> Self {
self
}
fn bright_black(self) -> Self {
self
}
fn yellow(self) -> Self {
self
}
}
#[cfg(not(feature = "color"))]
impl Colorize for &'static str {}
/// Initialize logger with fields
/// # Example
/// ```rust
/// use log::*;
///
/// clang_log::init(Level::Trace, "clang");
/// ```
pub fn init(min_level: Level, prog_name: &str) {
set_max_level(min_level.to_level_filter());
let logger = Logger {
min_level,
min_error_level: Level::Error,
prog_name: String::from(prog_name),
newline_sep: format!("\n{} ", " | ".white().bold()),
};
if set_boxed_logger(Box::new(logger)).is_err() {
debug!("Logger initialized twice");
}
}
/// Initialize logger with fields.
/// # Example
/// ```rust
/// use log::*;
///
/// clang_log::init_error(Level::Trace, Level::Warn, "clang");
/// ```
pub fn init_error(min_level: Level, min_error_level: Level, prog_name: &str) {
set_max_level(min_level.to_level_filter());
let logger = Logger {
min_level,
min_error_level,
prog_name: String::from(prog_name),
newline_sep: format!("\n{} ", " | ".white().bold()),
};
if set_boxed_logger(Box::new(logger)).is_err() {
debug!("Logger initialized twice");
}
}
/// Logger struct. Use `init()` instead, since `Logger` is useless without it being set as the logger, and `init()` does that anyway.
pub struct Logger {
/// Minimum level this logger will print. For example: `Level::Trace`
pub min_level: Level,
/// Minimum level this logger will print to stderr. For example: `Level::Warn`
pub min_error_level: Level,
/// Name of the program, set to "clang" in clang. (If clang used clang_log)
pub prog_name: String,
/// Constant newline separator, inserted between every newline. Avoids many allocations by storing this as a field.
pub newline_sep: String,
}
impl Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= self.min_level
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let msg = format!(
"{}: {} {}",
self.prog_name,
match record.level() {
Level::Error => {
"error:".red().bold()
}
Level::Warn => {
"warning:".bright_purple().bold()
}
Level::Info => {
"info:".bright_black().bold()
//"note".black().bold() // Clang Behavior
}
Level::Debug => {
"debug:".yellow().bold() // Clang doesn't have debug logs
}
Level::Trace => {
"trace:".white().bold() // Clang doesn't have trace logs
}
},
record.args().to_string().replace('\n', &self.newline_sep)
);
if record.level() >= self.min_error_level {
eprintln!("{msg}");
} else {
println!("{msg}");
}
}
fn flush(&self) {
stdout().flush().expect("could not flush to console"); // Just in case
}
}