use crate::DeferredNow;
#[cfg(feature = "colors")]
use ansi_term::{Color, Style};
use log::Record;
use std::thread;
use time::{format_description::FormatItem, macros::format_description};
pub const TS_DASHES_BLANK_COLONS_DOT_BLANK: &[FormatItem<'static>] = format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:6] \
[offset_hour sign:mandatory]:[offset_minute]"
);
pub fn default_format(
w: &mut dyn std::io::Write,
_now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"{} [{}] {}",
record.level(),
record.module_path().unwrap_or("<unnamed>"),
record.args()
)
}
#[allow(clippy::doc_markdown)]
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
pub fn colored_default_format(
w: &mut dyn std::io::Write,
_now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
let level = record.level();
write!(
w,
"{} [{}] {}",
style(level).paint(level.to_string()),
record.module_path().unwrap_or("<unnamed>"),
style(level).paint(record.args().to_string())
)
}
pub fn opt_format(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"[{}] {} [{}:{}] {}",
now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK),
record.level(),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
}
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
pub fn colored_opt_format(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
let level = record.level();
write!(
w,
"[{}] {} [{}:{}] {}",
style(level).paint(now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK)),
style(level).paint(level.to_string()),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
style(level).paint(&record.args().to_string())
)
}
pub fn detailed_format(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"[{}] {} [{}] {}:{}: {}",
now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK),
record.level(),
record.module_path().unwrap_or("<unnamed>"),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
}
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
pub fn colored_detailed_format(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
let level = record.level();
write!(
w,
"[{}] {} [{}] {}:{}: {}",
style(level).paint(now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK)),
style(level).paint(record.level().to_string()),
record.module_path().unwrap_or("<unnamed>"),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
style(level).paint(&record.args().to_string())
)
}
pub fn with_thread(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"[{}] T[{:?}] {} [{}:{}] {}",
now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK),
thread::current().name().unwrap_or("<unnamed>"),
record.level(),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
}
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
pub fn colored_with_thread(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
let level = record.level();
write!(
w,
"[{}] T[{:?}] {} [{}:{}] {}",
style(level).paint(now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK)),
style(level).paint(thread::current().name().unwrap_or("<unnamed>")),
style(level).paint(level.to_string()),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
style(level).paint(&record.args().to_string())
)
}
#[allow(clippy::missing_panics_doc)]
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
#[must_use]
pub fn style(level: log::Level) -> Style {
let palette = &*(PALETTE.read().unwrap());
match level {
log::Level::Error => palette.error,
log::Level::Warn => palette.warn,
log::Level::Info => palette.info,
log::Level::Debug => palette.debug,
log::Level::Trace => palette.trace,
}
}
#[cfg(feature = "colors")]
lazy_static::lazy_static! {
static ref PALETTE: std::sync::RwLock<Palette> = std::sync::RwLock::new(Palette::default());
}
#[cfg(feature = "colors")]
pub(crate) fn set_palette(input: &Option<String>) -> Result<(), std::num::ParseIntError> {
match std::env::var_os("FLEXI_LOGGER_PALETTE") {
Some(ref env_osstring) => {
*(PALETTE.write().unwrap()) = Palette::from(env_osstring.to_string_lossy().as_ref())?;
}
None => match input {
Some(ref input_string) => {
*(PALETTE.write().unwrap()) = Palette::from(input_string)?;
}
None => {}
},
}
Ok(())
}
#[cfg(feature = "colors")]
#[derive(Debug)]
struct Palette {
pub error: Style,
pub warn: Style,
pub info: Style,
pub debug: Style,
pub trace: Style,
}
#[cfg(feature = "colors")]
impl Palette {
fn default() -> Palette {
Palette {
error: Style::default().fg(Color::Fixed(196)).bold(),
warn: Style::default().fg(Color::Fixed(208)).bold(),
info: Style::default(),
debug: Style::default().fg(Color::Fixed(27)),
trace: Style::default().fg(Color::Fixed(8)),
}
}
fn from(palette_string: &str) -> Result<Palette, std::num::ParseIntError> {
let mut items = palette_string.split(';');
Ok(Palette {
error: parse_style(items.next().unwrap_or("196").trim())?,
warn: parse_style(items.next().unwrap_or("208").trim())?,
info: parse_style(items.next().unwrap_or("-").trim())?,
debug: parse_style(items.next().unwrap_or("27").trim())?,
trace: parse_style(items.next().unwrap_or("8").trim())?,
})
}
}
#[cfg(feature = "colors")]
fn parse_style(input: &str) -> Result<Style, std::num::ParseIntError> {
Ok(if input == "-" {
Style::new()
} else {
match input.strip_prefix('b') {
None => Style::new().fg(Color::Fixed(input.parse()?)),
Some(s) => Style::new().bold().fg(Color::Fixed(s.parse()?)),
}
})
}
#[cfg_attr(docsrs, doc(cfg(feature = "atty")))]
#[cfg(feature = "atty")]
#[derive(Clone, Copy)]
pub enum AdaptiveFormat {
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
Default,
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
Detailed,
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
Opt,
#[cfg_attr(docsrs, doc(cfg(feature = "colors")))]
#[cfg(feature = "colors")]
WithThread,
Custom(FormatFunction, FormatFunction),
}
#[cfg(feature = "atty")]
impl AdaptiveFormat {
#[must_use]
pub(crate) fn format_function(self, is_tty: bool) -> FormatFunction {
if is_tty {
match self {
#[cfg(feature = "colors")]
Self::Default => colored_default_format,
#[cfg(feature = "colors")]
Self::Detailed => colored_detailed_format,
#[cfg(feature = "colors")]
Self::Opt => colored_opt_format,
#[cfg(feature = "colors")]
Self::WithThread => colored_with_thread,
Self::Custom(_, colored) => colored,
}
} else {
match self {
#[cfg(feature = "colors")]
Self::Default => default_format,
#[cfg(feature = "colors")]
Self::Detailed => detailed_format,
#[cfg(feature = "colors")]
Self::Opt => opt_format,
#[cfg(feature = "colors")]
Self::WithThread => with_thread,
Self::Custom(uncolored, _) => uncolored,
}
}
}
}
pub type FormatFunction = fn(
write: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error>;