-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathformatter.rs
169 lines (155 loc) · 5.51 KB
/
formatter.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use clap::ValueEnum;
use console::Color::{Green, Magenta, White, Yellow};
use console::Style;
use std::fmt::Debug;
use tracing::field::{Field, Visit};
use tracing::span::Attributes;
use tracing::{Event, Subscriber};
use tracing::{Id, Level};
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;
/// Our custom `FormatEvent` implementation.
pub struct OrandaFormatter;
/// Our custom layer that takes care of recording span information.
pub struct CaptureFieldsLayer;
/// Visitor implementation for _span_ fields.
#[derive(Default, Clone)]
struct SpanVisitor {
pub prefix: Option<String>,
}
/// Visitor implementation for _event_ fields.
#[derive(Default)]
struct EventVisitor {
pub message: String,
pub success: bool,
}
/// Storage struct for our spans, accomplished by shoving it into the span `extensions` field.
struct SpanFieldStorage(SpanVisitor);
impl<S> Layer<S> for CaptureFieldsLayer
where
S: Subscriber,
S: for<'lookup> LookupSpan<'lookup>,
{
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
let mut visitor = SpanVisitor::default();
attrs.record(&mut visitor);
let storage = SpanFieldStorage(visitor);
let span = ctx.span(id).unwrap();
if span.name() == "workspace_page" {
let mut extensions = span.extensions_mut();
extensions.insert::<SpanFieldStorage>(storage);
}
}
}
impl<S, N> FormatEvent<S, N> for OrandaFormatter
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
/// oranda's custom tracing formatter. We apply our own custom formatting, but we also check
/// if the event has been fired inside of a `workspace_page` span, from which we get a prefix
/// to attach to the log message.
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> std::fmt::Result {
let warn_icon = Style::new().bold().fg(Yellow).apply_to("⚠");
let check_icon = Style::new().bold().fg(Green).apply_to("✓");
let arrow_icon = Style::new().bold().fg(White).apply_to("↪");
let mut message = EventVisitor::default();
let metadata = event.metadata();
event.record(&mut message);
if metadata.fields().field("message").is_some() {
// Handle the special success case first
let output_str = if message.success {
let style = Style::new().bold().fg(Green);
format!(
"{} >o_o< SUCCESS: {}",
check_icon,
style.apply_to(message.message)
)
} else if matches!(metadata.level(), &Level::INFO) {
let style = Style::new().bold().fg(White);
format!(
"{} >o_o< INFO: {}",
arrow_icon,
style.apply_to(message.message)
)
} else if matches!(metadata.level(), &Level::WARN) {
let style = Style::new().bold().fg(Yellow);
format!(
"{} >o_o< WARNING: {}",
warn_icon,
style.apply_to(message.message)
)
} else if matches!(metadata.level(), &Level::DEBUG) {
let style = Style::new().bold().fg(Magenta);
format!(
"{} >o_o< DEBUG: {}",
arrow_icon,
style.apply_to(message.message)
)
} else {
format!("TRACE: {}", message.message)
};
// Fetch our custom span fields, if any.
let fields = if let Some(span) = ctx.lookup_current() {
let extensions = span.extensions();
if let Some(storage) = extensions.get::<SpanFieldStorage>() {
let field_data = storage.0.clone();
Some(field_data)
} else {
None
}
} else {
None
};
if let Some(fields) = fields {
if let Some(prefix) = fields.prefix {
write!(
&mut writer,
"{:>15} {}",
console::style(prefix).magenta(),
output_str
)?;
} else {
write!(&mut writer, "{}", output_str)?;
}
} else {
write!(&mut writer, "{}", output_str)?;
}
}
writeln!(writer)
}
}
impl Visit for EventVisitor {
fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
if field.name() == "message" {
self.message = format!("{:?}", value);
}
if field.name() == "success" {
self.success = true;
}
}
}
impl Visit for SpanVisitor {
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "prefix" {
self.prefix = Some(value.to_string());
}
}
fn record_debug(&mut self, _field: &Field, _value: &dyn Debug) {}
}
/// Style of output we should produce
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum OutputFormat {
/// Human-readable output
Human,
/// Machine-readable JSON output
Json,
}