use std::{fmt::Write, io::Write as _};
use colored::{Color, Colorize};
use smallstr::SmallString;
use tracing::{
span::{Attributes, Id, Record},
Event, Subscriber,
};
use tracing_subscriber::{fmt::MakeWriter, layer::Context, registry::LookupSpan, Layer};
use crate::log::{
now_string, short_path, write_call, write_fields, Fields, LogLevelExt,
VerboseEventVisitor,
};
#[allow(dead_code)]
pub(super) struct VerbosePrettyLayer<W> {
make_writer: W,
}
#[allow(dead_code)]
impl<W> VerbosePrettyLayer<W> {
pub(super) fn new(
make_writer: W,
) -> Self {
Self { make_writer }
}
}
impl<S, W> Layer<S> for VerbosePrettyLayer<W>
where
S: Subscriber + for<'l> LookupSpan<'l>,
W: for<'w> MakeWriter<'w> + Send + Sync + 'static,
{
fn on_new_span(
&self,
attributes: &Attributes<'_>,
id: &Id,
tracing_context: Context<'_, S>,
) {
let Some(
span_ref,
) = tracing_context.span(id) else {
return;
};
let mut fields = Fields::default();
attributes.record(&mut fields);
span_ref.extensions_mut().insert(fields);
}
fn on_record(
&self,
id: &Id,
values: &Record<'_>,
tracing_context: Context<'_, S>,
) {
let Some(
span_ref,
) = tracing_context.span(id) else {
return;
};
let mut extensions = span_ref.extensions_mut();
if let Some(
fields,
) = extensions.get_mut::<Fields>() {
values.record(fields);
}
}
fn on_event(
&self,
event: &Event<'_>,
tracing_context: Context<'_, S>,
) {
let meta = event.metadata();
let mut event_visitor = VerboseEventVisitor::new(&tracing_context);
event.record(&mut event_visitor);
let mut out = String::with_capacity(512);
let mut line: SmallString<[u8; 16]> = SmallString::new();
let _ = write!(&mut line, "{}", meta.line().unwrap_or_default());
let _ = writeln!(
out,
"{:<5} {} {}{}{}",
meta.level().colored(),
now_string().color(Color::BrightBlack),
short_path(meta.file().unwrap_or("<unknown>"), 3).color(Color::BrightBlack),
":".color(Color::BrightBlack),
line.color(Color::BrightBlack),
);
if let Some(
scope,
) = tracing_context.event_scope(event) {
for span_ref in scope.from_root() {
let metadata = span_ref.metadata();
let file = metadata.file().unwrap_or("");
write_call(
&mut out,
meta.level(),
metadata.name(),
&short_path(file, 2),
metadata.line().unwrap_or_default(),
None,
);
}
}
if event_visitor.chain.is_empty() {
if let Some(
message,
) = &event_visitor.message && !message.is_empty() {
let _ = writeln!(
out,
"{} {}",
"\\_".color(meta.level().color()),
message.color(meta.level().color())
);
write_fields(&mut out, meta.level(), &event_visitor.fields);
}
} else {
if let Some(
message,
) = &event_visitor.message && !message.is_empty() {
let _ = writeln!(
out,
"{} {}",
"|-".color(meta.level().color()),
message.color(meta.level().color())
);
write_fields(&mut out, meta.level(), &event_visitor.fields);
}
}
let last_idx = event_visitor
.chain
.iter()
.rposition(|chain_link| {
if let Some(
message,
) = &chain_link.message && !message.is_empty() {
true
} else {
false
}
});
for (i, link) in event_visitor.chain.iter().enumerate() {
if let Some(
frame,
) = &link.frame {
write_call(
&mut out,
meta.level(),
frame.name,
&frame.file,
frame.line,
Some(&frame.fields),
);
}
if let Some(
message,
) = &link.message {
if Some(i) == last_idx {
let _ = writeln!(
out,
"{} {}",
"\\_".color(meta.level().color()),
message.color(meta.level().color())
);
} else {
let _ = writeln!(
out,
"{} {}",
"|-".color(meta.level().color()),
message.color(meta.level().color())
);
}
}
}
let _ = writeln!(out);
let mut w = self.make_writer.make_writer_for(meta);
let _ = w.write_all(out.as_bytes());
}
}