1use std::fmt;
2
3use tracing::field::Field;
4use tracing::{Event, Level, Metadata, Subscriber};
5use tracing::span::{Attributes, Id, Record};
6use tracing_subscriber::layer::{Layer, Context};
7use tracing_subscriber::registry::LookupSpan;
8use tracing_subscriber::field::RecordFields;
9
10use yansi::{Paint, Painted};
11
12use crate::util::Formatter;
13use crate::trace::subscriber::{Data, RecordDisplay, RocketFmt};
14
15#[derive(Debug, Default, Copy, Clone)]
16pub struct Pretty {
17 depth: u32,
18}
19
20impl RocketFmt<Pretty> {
21 fn indent(&self) -> &'static str {
22 static INDENT: &[&str] = &["", " ", " "];
23 INDENT.get(self.state().depth as usize).copied().unwrap_or(" ")
24 }
25
26 fn marker(&self) -> &'static str {
27 static MARKER: &[&str] = &["", ">> ", ":: "];
28 MARKER.get(self.state().depth as usize).copied().unwrap_or("-- ")
29 }
30
31 fn emoji(&self, _emoji: &'static str) -> Painted<&'static str> {
32 #[cfg(windows)] { "".paint(self.style).mask() }
33 #[cfg(not(windows))] { _emoji.paint(self.style).mask() }
34 }
35
36 fn prefix<'a>(&self, meta: &'a Metadata<'_>) -> impl fmt::Display + 'a {
37 let (i, m, s) = (self.indent(), self.marker(), self.style(meta));
38 Formatter(move |f| match *meta.level() {
39 Level::WARN => write!(f, "{i}{m}{} ", "warning:".paint(s).bold()),
40 Level::ERROR => write!(f, "{i}{m}{} ", "error:".paint(s).bold()),
41 Level::INFO => write!(f, "{i}{m}"),
42 level => write!(f, "{i}{m}[{} {}] ", level.paint(s).bold(), meta.target()),
43 })
44 }
45
46 fn print_pretty<F: RecordFields>(&self, m: &Metadata<'_>, data: F) {
47 let prefix = self.prefix(m);
48 let cont_prefix = Formatter(|f| {
49 let style = self.style(m);
50 write!(f, "{}{} ", self.indent(), "++".paint(style).dim())
51 });
52
53 self.print(&prefix, &cont_prefix, m, data);
54 }
55
56 fn print_fields<F>(&self, metadata: &Metadata<'_>, fields: F)
57 where F: RecordFields
58 {
59 let style = self.style(metadata);
60 let prefix = self.prefix(metadata);
61 fields.record_display(|key: &Field, value: &dyn fmt::Display| {
62 if key.name() != "message" {
63 println!("{prefix}{}: {}", key.paint(style), value.paint(style).primary());
64 }
65 })
66 }
67}
68
69impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for RocketFmt<Pretty> {
70 fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
71 self.filter.would_enable(metadata.target(), metadata.level())
72 }
73
74 fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
75 let (meta, data) = (event.metadata(), Data::new(event));
76 let style = self.style(meta);
77 match meta.name() {
78 "config" => self.print_fields(meta, event),
79 "liftoff" => {
80 let prefix = self.prefix(meta);
81 println!("{prefix}{}{} {}", self.emoji("🚀 "),
82 "Rocket has launched on".paint(style).primary().bold(),
83 &data["endpoint"].paint(style).primary().bold().underline());
84 },
85 "route" => println!("{}", Formatter(|f| {
86 write!(f, "{}{}{}: ", self.indent(), self.marker(), "route".paint(style))?;
87
88 let (base, mut relative) = (&data["uri.base"], &data["uri.unmounted"]);
89 if base.ends_with('/') && relative.starts_with('/') {
90 relative = &relative[1..];
91 }
92
93 write!(f, "{:>3} {} {}{}",
94 &data["rank"].paint(style.bright().dim()),
95 &data["method"].paint(style.bold()),
96 base.paint(style.primary().underline()),
97 relative.paint(style.primary()),
98 )?;
99
100 if let Some(name) = data.get("name") {
101 write!(f, " ({}", name.paint(style.bold().bright()))?;
102
103 if let Some(location) = data.get("location") {
104 write!(f, " {}", location.paint(style.dim()))?;
105 }
106
107 write!(f, ")")?;
108 }
109
110 Ok(())
111 })),
112 "catcher" => println!("{}", Formatter(|f| {
113 write!(f, "{}{}{}: ", self.indent(), self.marker(), "catcher".paint(style))?;
114
115 match data.get("code") {
116 Some(code) => write!(f, "{} ", code.paint(style.bold()))?,
117 None => write!(f, "{} ", "default".paint(style.bold()))?,
118 }
119
120 write!(f, "{}", &data["uri.base"].paint(style.primary()))?;
121 if let Some(name) = data.get("name") {
122 write!(f, " ({}", name.paint(style.bold().bright()))?;
123
124 if let Some(location) = data.get("location") {
125 write!(f, " {}", location.paint(style.dim()))?;
126 }
127
128 write!(f, ")")?;
129 }
130
131 Ok(())
132 })),
133 "header" => println!("{}{}{}: {}: {}",
134 self.indent(), self.marker(), "header".paint(style),
135 &data["name"].paint(style.bold()),
136 &data["value"].paint(style.primary()),
137 ),
138 "fairing" => println!("{}{}{}: {} {}",
139 self.indent(), self.marker(), "fairing".paint(style),
140 &data["name"].paint(style.bold()),
141 &data["kind"].paint(style.primary().dim()),
142 ),
143 _ => self.print_pretty(meta, event),
144 }
145 }
146
147 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctxt: Context<'_, S>) {
148 let data = Data::new(attrs);
149 let span = ctxt.span(id).expect("new_span: span does not exist");
150 if &data["count"] != "0" {
151 let name = span.name();
152 let icon = match name {
153 "config" => "🔧 ",
154 "routes" => "📬 ",
155 "catchers" => "🚧 ",
156 "fairings" => "📦 ",
157 "shield" => "🛡️ ",
158 "templating" => "📐 ",
159 "request" => "● ",
160 _ => "",
161 };
162
163 let meta = span.metadata();
164 let style = self.style(meta);
165 let emoji = self.emoji(icon);
166 let name = name.paint(style).bold();
167
168 let fields = self.compact_fields(meta, attrs);
169 let prefix = self.prefix(meta);
170 let fieldless_prefix = Formatter(|f| write!(f, "{prefix}{emoji}{name} "));
171 let field_prefix = Formatter(|f| write!(f, "{prefix}{emoji}{name} ({fields}) "));
172
173 if self.has_message(meta) && self.has_data_fields(meta) {
174 print!("{}", self.message(&field_prefix, &fieldless_prefix, meta, attrs));
175 } else if self.has_message(meta) {
176 print!("{}", self.message(&fieldless_prefix, &fieldless_prefix, meta, attrs));
177 } else if self.has_data_fields(meta) {
178 println!("{field_prefix}");
179 } else {
180 println!("{fieldless_prefix}");
181 }
182 }
183
184 span.extensions_mut().replace(data);
185 }
186
187 fn on_record(&self, id: &Id, values: &Record<'_>, ctxt: Context<'_, S>) {
188 let span = ctxt.span(id).expect("new_span: span does not exist");
189 match span.extensions_mut().get_mut::<Data>() {
190 Some(data) => values.record(data),
191 None => span.extensions_mut().insert(Data::new(values)),
192 }
193
194 let meta = span.metadata();
195 println!("{}{}", self.prefix(meta), self.compact_fields(meta, values));
196 }
197
198 fn on_enter(&self, _: &Id, _: Context<'_, S>) {
199 self.update_state(|state| state.depth = state.depth.saturating_add(1));
200 }
201
202 fn on_exit(&self, _: &Id, _: Context<'_, S>) {
203 self.update_state(|state| state.depth = state.depth.saturating_sub(1));
204 }
205}