diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/tracing-tree/src | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tracing-tree/src')
-rw-r--r-- | vendor/tracing-tree/src/format.rs | 425 | ||||
-rw-r--r-- | vendor/tracing-tree/src/lib.rs | 395 |
2 files changed, 820 insertions, 0 deletions
diff --git a/vendor/tracing-tree/src/format.rs b/vendor/tracing-tree/src/format.rs new file mode 100644 index 000000000..067ea9771 --- /dev/null +++ b/vendor/tracing-tree/src/format.rs @@ -0,0 +1,425 @@ +use ansi_term::Color; +use std::{ + fmt::{self, Write as _}, + io, +}; +use tracing_core::{ + field::{Field, Visit}, + Level, +}; + +pub(crate) const LINE_VERT: &str = "│"; +const LINE_HORIZ: &str = "─"; +pub(crate) const LINE_BRANCH: &str = "├"; +pub(crate) const LINE_CLOSE: &str = "┘"; +pub(crate) const LINE_OPEN: &str = "┐"; + +#[derive(Copy, Clone)] +pub(crate) enum SpanMode { + PreOpen, + Open { verbose: bool }, + Close { verbose: bool }, + PostClose, + Event, +} + +#[derive(Debug)] +pub struct Config { + /// Whether to use colors. + pub ansi: bool, + /// Whether an ascii art tree is used or (if false) whether to just use whitespace indent + pub indent_lines: bool, + /// The amount of chars to indent. + pub indent_amount: usize, + /// Whether to show the module paths. + pub targets: bool, + /// Whether to show thread ids. + pub render_thread_ids: bool, + /// Whether to show thread names. + pub render_thread_names: bool, + /// Specifies after how many indentation levels we will wrap back around to zero + pub wraparound: usize, + /// Whether to print the current span before activating a new one + pub verbose_entry: bool, + /// Whether to print the current span before exiting it. + pub verbose_exit: bool, + /// Whether to print squiggly brackets (`{}`) around the list of fields in a span. + pub bracketed_fields: bool, +} + +impl Config { + pub fn with_ansi(self, ansi: bool) -> Self { + Self { ansi, ..self } + } + + pub fn with_indent_lines(self, indent_lines: bool) -> Self { + Self { + indent_lines, + ..self + } + } + + pub fn with_targets(self, targets: bool) -> Self { + Self { targets, ..self } + } + + pub fn with_thread_ids(self, render_thread_ids: bool) -> Self { + Self { + render_thread_ids, + ..self + } + } + + pub fn with_thread_names(self, render_thread_names: bool) -> Self { + Self { + render_thread_names, + ..self + } + } + + pub fn with_wraparound(self, wraparound: usize) -> Self { + Self { wraparound, ..self } + } + + pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { + Self { + verbose_entry, + ..self + } + } + + pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { + Self { + verbose_exit, + ..self + } + } + + pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { + Self { + bracketed_fields, + ..self + } + } + + pub(crate) fn prefix(&self) -> String { + let mut buf = String::new(); + if self.render_thread_ids { + write!(buf, "{:?}", std::thread::current().id()).unwrap(); + if buf.ends_with(')') { + buf.truncate(buf.len() - 1); + } + if buf.starts_with("ThreadId(") { + buf.drain(0.."ThreadId(".len()); + } + } + if self.render_thread_names { + if let Some(name) = std::thread::current().name() { + if self.render_thread_ids { + buf.push(':'); + } + buf.push_str(name); + } + } + buf + } +} + +impl Default for Config { + fn default() -> Self { + Self { + ansi: true, + indent_lines: false, + indent_amount: 2, + targets: false, + render_thread_ids: false, + render_thread_names: false, + wraparound: usize::max_value(), + verbose_entry: false, + verbose_exit: false, + bracketed_fields: false, + } + } +} + +#[derive(Debug)] +pub struct Buffers { + pub current_buf: String, + pub indent_buf: String, +} + +impl Buffers { + pub fn new() -> Self { + Self { + current_buf: String::new(), + indent_buf: String::new(), + } + } + + pub fn flush_current_buf(&mut self, mut writer: impl io::Write) { + write!(writer, "{}", &self.current_buf).unwrap(); + self.current_buf.clear(); + } + + pub fn flush_indent_buf(&mut self) { + self.current_buf.push_str(&self.indent_buf); + self.indent_buf.clear(); + } + + pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) { + self.current_buf.push('\n'); + let prefix = config.prefix(); + + // Render something when wraparound occurs so the user is aware of it + if config.indent_lines { + match style { + SpanMode::Close { .. } | SpanMode::PostClose => { + if indent > 0 && (indent + 1) % config.wraparound == 0 { + self.indent_buf.push_str(&prefix); + for _ in 0..(indent % config.wraparound * config.indent_amount) { + self.indent_buf.push_str(LINE_HORIZ); + } + self.indent_buf.push_str(LINE_OPEN); + self.indent_buf.push('\n'); + } + } + _ => {} + } + } + + indent_block( + &mut self.current_buf, + &mut self.indent_buf, + indent % config.wraparound, + config.indent_amount, + config.indent_lines, + &prefix, + style, + ); + self.current_buf.clear(); + self.flush_indent_buf(); + + // Render something when wraparound occurs so the user is aware of it + if config.indent_lines { + match style { + SpanMode::PreOpen | SpanMode::Open { .. } => { + if indent > 0 && (indent + 1) % config.wraparound == 0 { + self.current_buf.push_str(&prefix); + for _ in 0..(indent % config.wraparound * config.indent_amount) { + self.current_buf.push_str(LINE_HORIZ); + } + self.current_buf.push_str(LINE_CLOSE); + self.current_buf.push('\n'); + } + } + _ => {} + } + } + } +} + +pub struct FmtEvent<'a> { + pub bufs: &'a mut Buffers, + pub comma: bool, +} + +impl<'a> Visit for FmtEvent<'a> { + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + let buf = &mut self.bufs.current_buf; + let comma = if self.comma { "," } else { "" }; + match field.name() { + "message" => { + write!(buf, "{} {:?}", comma, value).unwrap(); + self.comma = true; + } + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + name if name.starts_with("log.") => {} + name => { + write!(buf, "{} {}={:?}", comma, name, value).unwrap(); + self.comma = true; + } + } + } +} + +pub struct ColorLevel<'a>(pub &'a Level); + +impl<'a> fmt::Display for ColorLevel<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self.0 { + Level::TRACE => Color::Purple.bold().paint("TRACE"), + Level::DEBUG => Color::Blue.bold().paint("DEBUG"), + Level::INFO => Color::Green.bold().paint(" INFO"), + Level::WARN => Color::RGB(252, 234, 160).bold().paint(" WARN"), // orange + Level::ERROR => Color::Red.bold().paint("ERROR"), + } + .fmt(f) + } +} + +fn indent_block_with_lines( + lines: &[&str], + buf: &mut String, + indent: usize, + indent_amount: usize, + prefix: &str, + style: SpanMode, +) { + let indent_spaces = indent * indent_amount; + if lines.is_empty() { + return; + } else if indent_spaces == 0 { + for line in lines { + buf.push_str(prefix); + // The first indent is special, we only need to print open/close and nothing else + if indent == 0 { + match style { + SpanMode::Open { .. } => buf.push_str(LINE_OPEN), + SpanMode::Close { .. } => buf.push_str(LINE_CLOSE), + SpanMode::PreOpen | SpanMode::PostClose => {} + SpanMode::Event => {} + } + } + buf.push_str(line); + buf.push('\n'); + } + return; + } + let mut s = String::with_capacity(indent_spaces + prefix.len()); + s.push_str(prefix); + + // instead of using all spaces to indent, draw a vertical line at every indent level + // up until the last indent + for i in 0..(indent_spaces - indent_amount) { + if i % indent_amount == 0 { + s.push_str(LINE_VERT); + } else { + s.push(' '); + } + } + + // draw branch + buf.push_str(&s); + + match style { + SpanMode::PreOpen => { + buf.push_str(LINE_BRANCH); + for _ in 1..(indent_amount / 2) { + buf.push_str(LINE_HORIZ); + } + buf.push_str(LINE_OPEN); + } + SpanMode::Open { verbose: false } => { + buf.push_str(LINE_BRANCH); + for _ in 1..indent_amount { + buf.push_str(LINE_HORIZ); + } + buf.push_str(LINE_OPEN); + } + SpanMode::Open { verbose: true } => { + buf.push_str(LINE_VERT); + for _ in 1..(indent_amount / 2) { + buf.push(' '); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push('└'); + } + for _ in (indent_amount / 2)..(indent_amount - 1) { + buf.push_str(LINE_HORIZ); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push_str(LINE_OPEN); + } else { + buf.push_str(LINE_VERT); + } + } + SpanMode::Close { verbose: false } => { + buf.push_str(LINE_BRANCH); + for _ in 1..indent_amount { + buf.push_str(LINE_HORIZ); + } + buf.push_str(LINE_CLOSE); + } + SpanMode::Close { verbose: true } => { + buf.push_str(LINE_VERT); + for _ in 1..(indent_amount / 2) { + buf.push(' '); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push('┌'); + } + for _ in (indent_amount / 2)..(indent_amount - 1) { + buf.push_str(LINE_HORIZ); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push_str(LINE_CLOSE); + } else { + buf.push_str(LINE_VERT); + } + } + SpanMode::PostClose => { + buf.push_str(LINE_BRANCH); + for _ in 1..(indent_amount / 2) { + buf.push_str(LINE_HORIZ); + } + buf.push_str(LINE_CLOSE); + } + SpanMode::Event => { + buf.push_str(LINE_BRANCH); + + // add `indent_amount - 1` horizontal lines before the span/event + for _ in 0..(indent_amount - 1) { + buf.push_str(LINE_HORIZ); + } + } + } + buf.push_str(lines[0]); + buf.push('\n'); + + // add the rest of the indentation, since we don't want to draw horizontal lines + // for subsequent lines + for i in 0..indent_amount { + if i % indent_amount == 0 { + s.push_str(LINE_VERT); + } else { + s.push(' '); + } + } + + // add all of the actual content, with each line preceded by the indent string + for line in &lines[1..] { + buf.push_str(&s); + buf.push_str(line); + buf.push('\n'); + } +} + +fn indent_block( + block: &mut String, + buf: &mut String, + indent: usize, + indent_amount: usize, + indent_lines: bool, + prefix: &str, + style: SpanMode, +) { + let lines: Vec<&str> = block.lines().collect(); + let indent_spaces = indent * indent_amount; + buf.reserve(block.len() + (lines.len() * indent_spaces)); + if indent_lines { + indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style); + } else { + let indent_str = String::from(" ").repeat(indent_spaces); + for line in lines { + buf.push_str(prefix); + buf.push_str(&indent_str); + buf.push_str(line); + buf.push('\n'); + } + } +} diff --git a/vendor/tracing-tree/src/lib.rs b/vendor/tracing-tree/src/lib.rs new file mode 100644 index 000000000..dbacaa57e --- /dev/null +++ b/vendor/tracing-tree/src/lib.rs @@ -0,0 +1,395 @@ +pub(crate) mod format; + +use ansi_term::{Color, Style}; +use format::{Buffers, ColorLevel, Config, FmtEvent, SpanMode}; +use std::{ + fmt::{self, Write as _}, + io, + sync::Mutex, + time::Instant, +}; +use tracing_core::{ + field::{Field, Visit}, + span::{Attributes, Id}, + Event, Subscriber, +}; +#[cfg(feature = "tracing-log")] +use tracing_log::NormalizeEvent; +use tracing_subscriber::{ + fmt::MakeWriter, + layer::{Context, Layer}, + registry::{self, LookupSpan}, +}; + +pub(crate) struct Data { + start: Instant, + kvs: Vec<(&'static str, String)>, +} + +impl Data { + pub fn new(attrs: &Attributes<'_>) -> Self { + let mut span = Self { + start: Instant::now(), + kvs: Vec::new(), + }; + attrs.record(&mut span); + span + } +} + +impl Visit for Data { + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + self.kvs.push((field.name(), format!("{:?}", value))) + } +} +#[derive(Debug)] +pub struct HierarchicalLayer<W = fn() -> io::Stderr> +where + W: for<'writer> MakeWriter<'writer> + 'static, +{ + make_writer: W, + bufs: Mutex<Buffers>, + config: Config, +} + +impl Default for HierarchicalLayer { + fn default() -> Self { + Self::new(2) + } +} + +impl HierarchicalLayer<fn() -> io::Stderr> { + pub fn new(indent_amount: usize) -> Self { + let ansi = atty::is(atty::Stream::Stderr); + let config = Config { + ansi, + indent_amount, + ..Default::default() + }; + Self { + make_writer: io::stderr, + bufs: Mutex::new(Buffers::new()), + config, + } + } +} + +impl<W> HierarchicalLayer<W> +where + W: for<'writer> MakeWriter<'writer> + 'static, +{ + /// Enables terminal colors, boldness and italics. + pub fn with_ansi(self, ansi: bool) -> Self { + Self { + config: self.config.with_ansi(ansi), + ..self + } + } + + pub fn with_writer<W2>(self, make_writer: W2) -> HierarchicalLayer<W2> + where + W2: for<'writer> MakeWriter<'writer>, + { + HierarchicalLayer { + make_writer, + config: self.config, + bufs: self.bufs, + } + } + + pub fn with_indent_amount(self, indent_amount: usize) -> Self { + let config = Config { + indent_amount, + ..self.config + }; + Self { config, ..self } + } + + /// Renders an ascii art tree instead of just using whitespace indentation. + pub fn with_indent_lines(self, indent_lines: bool) -> Self { + Self { + config: self.config.with_indent_lines(indent_lines), + ..self + } + } + + /// Whether to render the event and span targets. Usually targets are the module path to the + /// event/span macro invocation. + pub fn with_targets(self, targets: bool) -> Self { + Self { + config: self.config.with_targets(targets), + ..self + } + } + + /// Whether to render the thread id in the beginning of every line. This is helpful to + /// untangle the tracing statements emitted by each thread. + pub fn with_thread_ids(self, thread_ids: bool) -> Self { + Self { + config: self.config.with_thread_ids(thread_ids), + ..self + } + } + + /// Whether to render the thread name in the beginning of every line. Not all threads have + /// names, but if they do, this may be more helpful than the generic thread ids. + pub fn with_thread_names(self, thread_names: bool) -> Self { + Self { + config: self.config.with_thread_names(thread_names), + ..self + } + } + + /// Resets the indentation to zero after `wraparound` indentation levels. + /// This is helpful if you expect very deeply nested spans as otherwise the indentation + /// just runs out of your screen. + pub fn with_wraparound(self, wraparound: usize) -> Self { + Self { + config: self.config.with_wraparound(wraparound), + ..self + } + } + + /// Whether to print the currently active span's message again before entering a new span. + /// This helps if the entry to the current span was quite a while back (and with scrolling + /// upwards in logs). + pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { + Self { + config: self.config.with_verbose_entry(verbose_entry), + ..self + } + } + + /// Whether to print the currently active span's message again before dropping it. + /// This helps if the entry to the current span was quite a while back (and with scrolling + /// upwards in logs). + pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { + Self { + config: self.config.with_verbose_exit(verbose_exit), + ..self + } + } + + /// Whether to print `{}` around the fields when printing a span. + /// This can help visually distinguish fields from the rest of the message. + pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { + Self { + config: self.config.with_bracketed_fields(bracketed_fields), + ..self + } + } + + fn styled(&self, style: Style, text: impl AsRef<str>) -> String { + if self.config.ansi { + style.paint(text.as_ref()).to_string() + } else { + text.as_ref().to_string() + } + } + + fn print_kvs<'a, I, V>(&self, buf: &mut impl fmt::Write, kvs: I) -> fmt::Result + where + I: IntoIterator<Item = (&'a str, V)>, + V: fmt::Display + 'a, + { + let mut kvs = kvs.into_iter(); + if let Some((k, v)) = kvs.next() { + if k == "message" { + write!(buf, "{}", v)?; + } else { + write!(buf, "{}={}", k, v)?; + } + } + for (k, v) in kvs { + write!(buf, ", {}={}", k, v)?; + } + Ok(()) + } + + fn write_span_info<S>(&self, id: &Id, ctx: &Context<S>, style: SpanMode) + where + S: Subscriber + for<'span> LookupSpan<'span>, + { + let span = ctx + .span(id) + .expect("in on_enter/on_exit but span does not exist"); + let ext = span.extensions(); + let data = ext.get::<Data>().expect("span does not have data"); + + let mut guard = self.bufs.lock().unwrap(); + let bufs = &mut *guard; + let mut current_buf = &mut bufs.current_buf; + + let indent = ctx + .lookup_current() + .as_ref() + .map(registry::SpanRef::scope) + .map(registry::Scope::from_root) + .into_iter() + .flatten() + .count(); + + if self.config.verbose_entry || matches!(style, SpanMode::Open { .. } | SpanMode::Event) { + if self.config.targets { + let target = span.metadata().target(); + write!( + &mut current_buf, + "{}::", + self.styled(Style::new().dimmed(), target,), + ) + .expect("Unable to write to buffer"); + } + + write!( + current_buf, + "{name}", + name = self.styled(Style::new().fg(Color::Green).bold(), span.metadata().name()) + ) + .unwrap(); + if self.config.bracketed_fields { + write!( + current_buf, + "{}", + self.styled(Style::new().fg(Color::Green).bold(), "{") // Style::new().fg(Color::Green).dimmed().paint("{") + ) + .unwrap(); + } else { + write!(current_buf, " ").unwrap(); + } + self.print_kvs(&mut current_buf, data.kvs.iter().map(|(k, v)| (*k, v))) + .unwrap(); + if self.config.bracketed_fields { + write!( + current_buf, + "{}", + self.styled(Style::new().fg(Color::Green).bold(), "}") // Style::new().dimmed().paint("}") + ) + .unwrap(); + } + } + + bufs.indent_current(indent, &self.config, style); + let writer = self.make_writer.make_writer(); + bufs.flush_current_buf(writer) + } +} + +impl<S, W> Layer<S> for HierarchicalLayer<W> +where + S: Subscriber + for<'span> LookupSpan<'span>, + W: for<'writer> MakeWriter<'writer> + 'static, +{ + fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) { + let span = ctx.span(id).expect("in new_span but span does not exist"); + if span.extensions().get::<Data>().is_none() { + let data = Data::new(attrs); + span.extensions_mut().insert(data); + } + + if self.config.verbose_exit { + if let Some(span) = span.parent() { + self.write_span_info(&span.id(), &ctx, SpanMode::PreOpen); + } + } + + self.write_span_info( + id, + &ctx, + SpanMode::Open { + verbose: self.config.verbose_entry, + }, + ); + } + + fn on_event(&self, event: &Event<'_>, ctx: Context<S>) { + let mut guard = self.bufs.lock().unwrap(); + let bufs = &mut *guard; + let mut event_buf = &mut bufs.current_buf; + + // printing the indentation + let indent = if ctx.current_span().id().is_some() { + // size hint isn't implemented on Scope. + ctx.event_scope(event) + .expect("Unable to get span scope; this is a bug") + .count() + } else { + 0 + }; + + // check if this event occurred in the context of a span. + // if it has, get the start time of this span. + let start = match ctx.current_span().id() { + Some(id) => match ctx.span(id) { + // if the event is in a span, get the span's starting point. + Some(ctx) => { + let ext = ctx.extensions(); + let data = ext + .get::<Data>() + .expect("Data cannot be found in extensions"); + Some(data.start) + } + None => None, + }, + None => None, + }; + if let Some(start) = start { + let elapsed = start.elapsed(); + write!( + &mut event_buf, + "{timestamp}{unit} ", + timestamp = self.styled(Style::new().dimmed(), elapsed.as_millis().to_string()), + unit = self.styled(Style::new().dimmed(), "ms"), + ) + .expect("Unable to write to buffer"); + } + + #[cfg(feature = "tracing-log")] + let normalized_meta = event.normalized_metadata(); + #[cfg(feature = "tracing-log")] + let metadata = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); + #[cfg(not(feature = "tracing-log"))] + let metadata = event.metadata(); + + let level = metadata.level(); + let level = if self.config.ansi { + ColorLevel(level).to_string() + } else { + level.to_string() + }; + write!(&mut event_buf, "{level}", level = level).expect("Unable to write to buffer"); + + if self.config.targets { + let target = metadata.target(); + write!( + &mut event_buf, + " {}", + self.styled(Style::new().dimmed(), target,), + ) + .expect("Unable to write to buffer"); + } + + let mut visitor = FmtEvent { comma: false, bufs }; + event.record(&mut visitor); + visitor + .bufs + .indent_current(indent, &self.config, SpanMode::Event); + let writer = self.make_writer.make_writer(); + bufs.flush_current_buf(writer) + } + + fn on_close(&self, id: Id, ctx: Context<S>) { + self.write_span_info( + &id, + &ctx, + SpanMode::Close { + verbose: self.config.verbose_exit, + }, + ); + + if self.config.verbose_exit { + if let Some(span) = ctx.span(&id).and_then(|span| span.parent()) { + self.write_span_info(&span.id(), &ctx, SpanMode::PostClose); + } + } + } +} |