#[cfg(feature = "ansi")]
use crate::nu_ansi_term::{Color, Style};
use std::{fmt, io};
use time::{format_description::FormatItem, formatting::Formattable, OffsetDateTime};
use tracing::{Level, Metadata};
use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
pub(crate) struct WriteAdaptor<'a> {
fmt_write: &'a mut dyn fmt::Write,
}
impl<'a> WriteAdaptor<'a> {
pub(crate) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self {
Self { fmt_write }
}
}
impl<'a> io::Write for WriteAdaptor<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let s =
std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
self.fmt_write
.write_str(s)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(s.as_bytes().len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<'a> fmt::Debug for WriteAdaptor<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("WriteAdaptor { .. }")
}
}
pub(crate) struct FmtLevel {
pub level: Level,
#[cfg(feature = "ansi")]
pub ansi: bool,
}
impl FmtLevel {
const TRACE_STR: &'static str = "T";
const DEBUG_STR: &'static str = "D";
const INFO_STR: &'static str = "I";
const WARN_STR: &'static str = "W";
const ERROR_STR: &'static str = "E";
pub(crate) fn format_level(level: Level, ansi: bool) -> FmtLevel {
#[cfg(not(feature = "ansi"))]
let _ = ansi;
FmtLevel {
level,
#[cfg(feature = "ansi")]
ansi,
}
}
}
impl fmt::Display for FmtLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "ansi")]
if self.ansi {
return match self.level {
Level::TRACE => write!(f, "{}", Color::Purple.paint(Self::TRACE_STR)),
Level::DEBUG => write!(f, "{}", Color::Blue.paint(Self::DEBUG_STR)),
Level::INFO => write!(f, "{}", Color::Green.paint(Self::INFO_STR)),
Level::WARN => write!(f, "{}", Color::Yellow.paint(Self::WARN_STR)),
Level::ERROR => write!(f, "{}", Color::Red.paint(Self::ERROR_STR)),
};
}
match self.level {
Level::TRACE => f.pad(Self::TRACE_STR),
Level::DEBUG => f.pad(Self::DEBUG_STR),
Level::INFO => f.pad(Self::INFO_STR),
Level::WARN => f.pad(Self::WARN_STR),
Level::ERROR => f.pad(Self::ERROR_STR),
}
}
}
#[derive(Clone, Debug)]
pub struct UtcTime<F = Vec<FormatItem<'static>>> {
format: F,
}
impl<F> FormatTime for UtcTime<F>
where
F: Formattable,
{
fn format_time(&self, writer: &mut Writer<'_>) -> fmt::Result {
let now = OffsetDateTime::now_utc();
#[cfg(feature = "ansi")]
if writer.has_ansi_escapes() {
let style = Style::new().dimmed();
write!(writer, "{}", style.prefix())?;
format_datetime(writer, now, &self.format)?;
write!(writer, "{}", style.suffix())?;
return Ok(());
}
format_datetime(writer, now, &self.format)
}
}
impl Default for UtcTime {
fn default() -> Self {
let format: Vec<FormatItem> = time::format_description::parse(
"[month][day] [hour]:[minute]:[second].[subsecond digits:6]",
)
.expect("Unable to make time formatter");
Self { format }
}
}
#[derive(Clone, Debug)]
pub struct LocalTime<F = Vec<FormatItem<'static>>> {
format: F,
}
impl Default for LocalTime {
fn default() -> Self {
let format: Vec<FormatItem> = time::format_description::parse(
"[month][day] [hour]:[minute]:[second].[subsecond digits:6]",
)
.expect("Unable to make time formatter");
Self { format }
}
}
impl<F> FormatTime for LocalTime<F>
where
F: Formattable,
{
fn format_time(&self, writer: &mut Writer<'_>) -> fmt::Result {
let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
#[cfg(feature = "ansi")]
if writer.has_ansi_escapes() {
let style = Style::new().dimmed();
write!(writer, "{}", style.prefix())?;
format_datetime(writer, now, &self.format)?;
write!(writer, "{}", style.suffix())?;
return Ok(());
}
format_datetime(writer, now, &self.format)
}
}
fn format_datetime(
into: &mut Writer<'_>,
now: OffsetDateTime,
fmt: &impl Formattable,
) -> fmt::Result {
let mut into = WriteAdaptor::new(into);
now.format_into(&mut into, fmt)
.map_err(|_| fmt::Error)
.map(|_| ())
}
pub(crate) struct FormatProcessData<'a> {
pub(crate) pid: u32,
pub(crate) thread_name: Option<&'a str>,
pub(crate) with_thread_names: bool,
pub(crate) metadata: &'a Metadata<'a>,
pub(crate) with_target: bool,
#[cfg(feature = "ansi")]
pub(crate) ansi: bool,
}
impl<'a> fmt::Display for FormatProcessData<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let thread_name = self.thread_name;
let target = self.metadata.target();
let file = self.metadata.file().unwrap_or("");
let line = match self.metadata.line() {
Some(line) => format!("{}", line),
None => String::new(),
};
write!(f, " {pid:>5}", pid = self.pid)?;
#[cfg(feature = "ansi")]
if self.ansi {
let style = Style::new().bold();
write!(f, "{}", style.prefix())?;
if let Some(name) = thread_name {
if self.with_thread_names {
write!(f, " {}", name)?
}
}
if self.with_target {
write!(f, " [{}]", target)?;
}
write!(f, " {file}:{line}", file = file, line = line)?;
write!(f, "{}", style.suffix())?;
return Ok(());
}
if let Some(name) = thread_name {
if self.with_thread_names {
write!(f, " {}", name)?
}
}
if self.with_target {
write!(f, " [{}]", target)?;
}
write!(f, " {file}:{line}", file = file, line = line)?;
Ok(())
}
}
pub(crate) struct FormatSpanFields<'a> {
span_name: &'static str,
fields: Option<&'a str>,
#[cfg(feature = "ansi")]
pub ansi: bool,
print_span_names: bool,
}
impl<'a> FormatSpanFields<'a> {
pub(crate) fn format_fields(
span_name: &'static str,
fields: Option<&'a str>,
ansi: bool,
print_span_names: bool,
) -> Self {
#[cfg(not(feature = "ansi"))]
let _ = ansi;
Self {
span_name,
fields,
#[cfg(feature = "ansi")]
ansi,
print_span_names,
}
}
}
impl<'a> fmt::Display for FormatSpanFields<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "ansi")]
if self.ansi {
let bold = Style::new().bold();
if self.print_span_names {
write!(f, "{}", bold.paint(self.span_name))?;
}
let italic = Style::new().italic();
if let Some(fields) = self.fields {
if self.print_span_names {
write!(f, "{{{}}}", italic.paint(fields))?;
} else {
write!(f, "{}", italic.paint(fields))?;
}
};
return Ok(());
}
if self.print_span_names {
write!(f, "{}", self.span_name)?;
}
if let Some(fields) = self.fields {
if self.print_span_names {
write!(f, "{{{}}}", fields)?;
} else {
write!(f, "{}", fields)?;
}
};
Ok(())
}
}