use crate::DeferredNow;
use log::Record;
use std::thread;
#[cfg(feature = "colors")]
use yansi::{Color, Paint, Style};
pub type FormatFunction = fn(
write: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error>;
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(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, level),
record.module_path().unwrap_or("<unnamed>"),
style(level, record.args())
)
}
pub fn opt_format(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"[{}] {} [{}:{}] {}",
now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z"),
record.level(),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
}
#[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, now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z")),
style(level, level),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
style(level, &record.args())
)
}
pub fn detailed_format(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"[{}] {} [{}] {}:{}: {}",
now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z"),
record.level(),
record.module_path().unwrap_or("<unnamed>"),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
}
#[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, now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z")),
style(level, record.level()),
record.module_path().unwrap_or("<unnamed>"),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
style(level, &record.args())
)
}
pub fn with_thread(
w: &mut dyn std::io::Write,
now: &mut DeferredNow,
record: &Record,
) -> Result<(), std::io::Error> {
write!(
w,
"[{}] T[{:?}] {} [{}:{}] {}",
now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z"),
thread::current().name().unwrap_or("<unnamed>"),
record.level(),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
&record.args()
)
}
#[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, now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z")),
style(level, thread::current().name().unwrap_or("<unnamed>")),
style(level, level),
record.file().unwrap_or("<unnamed>"),
record.line().unwrap_or(0),
style(level, &record.args())
)
}
#[cfg(feature = "colors")]
pub fn style<T>(level: log::Level, item: T) -> Paint<T> {
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,
}
.paint(item)
}
#[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::new(Color::Fixed(196)).bold(),
warn: Style::new(Color::Fixed(208)).bold(),
info: Style::new(Color::Unset),
debug: Style::new(Color::Fixed(7)),
trace: Style::new(Color::Fixed(8)),
}
}
fn from(palette: &str) -> Result<Palette, std::num::ParseIntError> {
let mut items = palette.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("7").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(Color::Unset)
} else {
Style::new(Color::Fixed(input.parse()?))
})
}
#[cfg(feature = "atty")]
#[derive(Clone, Copy)]
pub enum AdaptiveFormat {
#[cfg(feature = "colors")]
Default,
#[cfg(feature = "colors")]
Detailed,
#[cfg(feature = "colors")]
Opt,
#[cfg(feature = "colors")]
WithThread,
Custom(FormatFunction, FormatFunction),
}
#[cfg(feature = "atty")]
impl AdaptiveFormat {
#[must_use]
pub(crate) fn format_function(self, stream: Stream) -> FormatFunction {
if stream.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,
}
}
}
}
#[cfg(feature = "atty")]
#[derive(Clone, Copy)]
pub(crate) enum Stream {
StdOut,
StdErr,
}
#[cfg(feature = "atty")]
impl Stream {
#[must_use]
pub fn is_tty(self) -> bool {
match self {
Self::StdOut => atty::is(atty::Stream::Stdout),
Self::StdErr => atty::is(atty::Stream::Stderr),
}
}
}