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
|
//! Simple logger that logs either to stderr or to a file, using `tracing_subscriber`
//! filter syntax and `tracing_appender` for non blocking output.
use std::{
fmt,
fs::File,
io::{self, Stderr},
sync::Arc,
};
use rust_analyzer::Result;
use tracing::{level_filters::LevelFilter, Event, Subscriber};
use tracing_log::NormalizeEvent;
use tracing_subscriber::{
fmt::{
format::Writer, writer::BoxMakeWriter, FmtContext, FormatEvent, FormatFields,
FormattedFields, MakeWriter,
},
layer::SubscriberExt,
registry::LookupSpan,
util::SubscriberInitExt,
EnvFilter, Registry,
};
use tracing_tree::HierarchicalLayer;
pub(crate) struct Logger {
filter: EnvFilter,
file: Option<File>,
}
struct MakeWriterStderr;
impl<'a> MakeWriter<'a> for MakeWriterStderr {
type Writer = Stderr;
fn make_writer(&'a self) -> Self::Writer {
io::stderr()
}
}
impl Logger {
pub(crate) fn new(file: Option<File>, filter: Option<&str>) -> Logger {
let filter = filter.map_or(EnvFilter::default(), EnvFilter::new);
Logger { filter, file }
}
pub(crate) fn install(self) -> Result<()> {
// The meaning of CHALK_DEBUG I suspected is to tell chalk crates
// (i.e. chalk-solve, chalk-ir, chalk-recursive) how to filter tracing
// logs. But now we can only have just one filter, which means we have to
// merge chalk filter to our main filter (from RA_LOG env).
//
// The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`.
// As the value should only affect chalk crates, we'd better mannually
// specify the target. And for simplicity, CHALK_DEBUG only accept the value
// that specify level.
let chalk_level_dir = std::env::var("CHALK_DEBUG")
.map(|val| {
val.parse::<LevelFilter>().expect(
"invalid CHALK_DEBUG value, expect right log level (like debug or trace)",
)
})
.ok();
let chalk_layer = HierarchicalLayer::default()
.with_indent_lines(true)
.with_ansi(false)
.with_indent_amount(2)
.with_writer(io::stderr);
let writer = match self.file {
Some(file) => BoxMakeWriter::new(Arc::new(file)),
None => BoxMakeWriter::new(io::stderr),
};
let ra_fmt_layer =
tracing_subscriber::fmt::layer().event_format(LoggerFormatter).with_writer(writer);
match chalk_level_dir {
Some(val) => {
Registry::default()
.with(
self.filter
.add_directive(format!("chalk_solve={}", val).parse()?)
.add_directive(format!("chalk_ir={}", val).parse()?)
.add_directive(format!("chalk_recursive={}", val).parse()?),
)
.with(ra_fmt_layer)
.with(chalk_layer)
.init();
}
None => {
Registry::default().with(self.filter).with(ra_fmt_layer).init();
}
};
Ok(())
}
}
#[derive(Debug)]
struct LoggerFormatter;
impl<S, N> FormatEvent<S, N> for LoggerFormatter
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: Writer,
event: &Event<'_>,
) -> fmt::Result {
// Write level and target
let level = *event.metadata().level();
// If this event is issued from `log` crate, then the value of target is
// always "log". `tracing-log` has hard coded it for some reason, so we
// need to extract it using `normalized_metadata` method which is part of
// `tracing_log::NormalizeEvent`.
let target = match event.normalized_metadata() {
// This event is issued from `log` crate
Some(log) => log.target(),
None => event.metadata().target(),
};
write!(writer, "[{} {}] ", level, target)?;
// Write spans and fields of each span
ctx.visit_spans(|span| {
write!(writer, "{}", span.name())?;
let ext = span.extensions();
// `FormattedFields` is a a formatted representation of the span's
// fields, which is stored in its extensions by the `fmt` layer's
// `new_span` method. The fields will have been formatted
// by the same field formatter that's provided to the event
// formatter in the `FmtContext`.
let fields = &ext.get::<FormattedFields<N>>().expect("will never be `None`");
if !fields.is_empty() {
write!(writer, "{{{}}}", fields)?;
}
write!(writer, ": ")?;
Ok(())
})?;
// Write fields on the event
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer)
}
}
|