use crate::formatter::Formatter;
use crate::layer::{KeyValue, Tree, TreeAttrs, TreeEvent, TreeKind, TreeSpan};
use crate::tag::TagData;
use std::fmt;
use std::io::{self, Write};
use tracing::Level;
#[derive(Clone, Copy)]
pub struct Pretty {
_priv: (),
}
impl Pretty {
pub const fn new() -> Self {
Pretty { _priv: () }
}
}
impl Formatter for Pretty {
fn fmt(&self, tree: Tree, writer: &mut Vec<u8>) -> io::Result<()> {
let mut indent = Vec::with_capacity(0);
format_tree(&tree, None, &mut indent, writer)
}
}
#[derive(Copy, Clone)]
enum Edge {
Null,
Line,
Fork,
Turn,
}
impl Edge {
fn repr(&self) -> &'static str {
match self {
Self::Null => " ",
Self::Line => "│ ",
Self::Fork => "┝━ ",
Self::Turn => "┕━ ",
}
}
}
fn format_attrs(attrs: &TreeAttrs, writer: &mut Vec<u8>) -> io::Result<()> {
#[cfg(feature = "uuid")]
write!(writer, "{} ", attrs.uuid)?;
#[cfg(feature = "chrono")]
write!(writer, "{:<32} ", attrs.timestamp.to_rfc3339())?;
write!(writer, "{:<8} ", attrs.level)
}
fn format_indent(indent: &mut Vec<Edge>, writer: &mut Vec<u8>) -> io::Result<()> {
indent
.iter()
.try_for_each(|edge| writer.write_all(edge.repr().as_bytes()))
}
fn format_event(event: &TreeEvent, level: Level, writer: &mut Vec<u8>) -> io::Result<()> {
let tag = event.tag.unwrap_or_else(|| TagData::from(level));
write!(writer, "{} [{}]: {}", tag.icon, tag.message, event.message)?;
for KeyValue { key, value } in event.fields.iter() {
write!(writer, " | {}: {}", key, value)?;
}
writeln!(writer)
}
fn format_span(
span: &TreeSpan,
duration_root: Option<f64>,
indent: &mut Vec<Edge>,
writer: &mut Vec<u8>,
) -> io::Result<()> {
let duration_total = span.duration_total.as_nanos() as f64;
let duration_nested = span.duration_nested.as_nanos() as u64;
let duration_root = duration_root.unwrap_or(duration_total);
let load_total = 100.0 * duration_total / duration_root;
write!(
writer,
"{} [ {} | ",
span.name,
DurationDisplay(duration_total)
)?;
if duration_nested > 0 {
let load_direct = 100.0 * (duration_total - duration_nested as f64) / duration_root;
write!(writer, "{:.3}% / ", load_direct)?;
}
writeln!(writer, "{:.3}% ]", load_total)?;
if let Some((last, remaining)) = span.children.split_last() {
match indent.last_mut() {
Some(edge @ Edge::Turn) => *edge = Edge::Null,
Some(edge @ Edge::Fork) => *edge = Edge::Line,
_ => {}
}
indent.push(Edge::Fork);
for tree in remaining {
if let Some(edge) = indent.last_mut() {
*edge = Edge::Turn;
}
format_tree(tree, Some(duration_root), indent, writer)?;
}
if let Some(edge) = indent.last_mut() {
*edge = Edge::Turn;
}
format_tree(last, Some(duration_root), indent, writer)?;
indent.pop();
}
Ok(())
}
fn format_tree(
tree: &Tree,
duration_root: Option<f64>,
indent: &mut Vec<Edge>,
writer: &mut Vec<u8>,
) -> io::Result<()> {
format_attrs(&tree.attrs, writer)?;
format_indent(indent, writer)?;
match &tree.kind {
TreeKind::Event(event) => format_event(event, tree.attrs.level, writer),
TreeKind::Span(span) => format_span(span, duration_root, indent, writer),
}
}
struct DurationDisplay(f64);
impl fmt::Display for DurationDisplay {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut t = self.0;
for unit in ["ns", "µs", "ms", "s"] {
if t < 10.0 {
return write!(f, "{:.2}{}", t, unit);
} else if t < 100.0 {
return write!(f, "{:.1}{}", t, unit);
} else if t < 1000.0 {
return write!(f, "{:.0}{}", t, unit);
}
t /= 1000.0;
}
write!(f, "{:.0}s", t * 1000.0)
}
}