1use std::fmt;
2
3use jiff::Timestamp;
4use owo_colors::OwoColorize;
5use tracing::{Event, Subscriber, field::Field};
6use tracing_subscriber::field::MakeExt;
7use tracing_subscriber::fmt::format::{self, Writer};
8use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
9use tracing_subscriber::registry::LookupSpan;
10
11pub struct UvFormat {
13 pub display_timestamp: bool,
14 pub display_level: bool,
15 pub show_spans: bool,
16}
17
18impl Default for UvFormat {
19 fn default() -> Self {
21 Self {
22 display_timestamp: false,
23 display_level: true,
24 show_spans: false,
25 }
26 }
27}
28
29impl<S, N> FormatEvent<S, N> for UvFormat
31where
32 S: Subscriber + for<'a> LookupSpan<'a>,
33 N: for<'a> FormatFields<'a> + 'static,
34{
35 fn format_event(
36 &self,
37 ctx: &FmtContext<'_, S, N>,
38 mut writer: Writer<'_>,
39 event: &Event<'_>,
40 ) -> fmt::Result {
41 let meta = event.metadata();
42 let ansi = writer.has_ansi_escapes();
43
44 if self.display_timestamp {
45 if ansi {
46 write!(writer, "{} ", Timestamp::now().dimmed())?;
47 } else {
48 write!(writer, "{} ", Timestamp::now())?;
49 }
50 }
51
52 if self.display_level {
53 let level = meta.level();
54 if ansi {
56 match *level {
57 tracing::Level::TRACE => write!(writer, "{} ", level.purple())?,
58 tracing::Level::DEBUG => write!(writer, "{} ", level.blue())?,
59 tracing::Level::INFO => write!(writer, "{} ", level.green())?,
60 tracing::Level::WARN => write!(writer, "{} ", level.yellow())?,
61 tracing::Level::ERROR => write!(writer, "{} ", level.red())?,
62 }
63 } else {
64 write!(writer, "{level} ")?;
65 }
66 }
67
68 if self.show_spans {
69 let span = event.parent();
70 let mut seen = false;
71
72 let span = span
73 .and_then(|id| ctx.span(id))
74 .or_else(|| ctx.lookup_current());
75
76 let scope = span.into_iter().flat_map(|span| span.scope().from_root());
77
78 for span in scope {
79 seen = true;
80 if ansi {
81 write!(writer, "{}:", span.metadata().name().bold())?;
82 } else {
83 write!(writer, "{}:", span.metadata().name())?;
84 }
85 }
86
87 if seen {
88 writer.write_char(' ')?;
89 }
90 }
91
92 ctx.field_format().format_fields(writer.by_ref(), event)?;
93
94 writeln!(writer)
95 }
96}
97
98pub fn uv_fields() -> impl for<'writer> FormatFields<'writer> {
104 format::debug_fn(format_field)
105 .display_messages()
106 .delimited(" ")
107}
108
109fn format_field(writer: &mut Writer<'_>, field: &Field, value: &dyn fmt::Debug) -> fmt::Result {
110 let field = field.name();
114 if field.starts_with("log.") {
115 return Ok(());
116 }
117
118 let value = format!("{value:?}");
119 let value = anstream::adapter::strip_str(&value);
120
121 if field == "message" {
122 write!(writer, "{value}")
123 } else {
124 write!(
125 writer,
126 "{}={value}",
127 field.strip_prefix("r#").unwrap_or(field)
129 )
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use tracing::{Callsite, Event, Level, field::Value, metadata::Kind};
136 use tracing_subscriber::fmt::FormatFields;
137 use tracing_subscriber::fmt::format::Writer;
138
139 use super::uv_fields;
140
141 #[test]
142 fn strips_ansi_from_message_fields() {
143 let callsite = tracing::callsite! {
144 name: "event",
145 kind: Kind::EVENT,
146 level: Level::TRACE,
147 fields: message
148 };
149 let metadata = callsite.metadata();
150 let message = format_args!("Error trace: {}", "\x1b[36m\x1b[1mhint\x1b[0m");
151 let values = [Some(&message as &dyn Value)];
152 let fields = metadata.fields().value_set_all(&values);
153 let event = Event::new(metadata, &fields);
154 let mut output = String::new();
155
156 uv_fields()
157 .format_fields(Writer::new(&mut output), event)
158 .expect("field formatting should succeed");
159
160 assert_eq!(output, "Error trace: hint");
161 }
162}