1 //! Simple logger that logs either to stderr or to a file, using `tracing_subscriber`
2 //! filter syntax and `tracing_appender` for non blocking output.
11 use rust_analyzer::Result;
12 use tracing::{level_filters::LevelFilter, Event, Subscriber};
13 use tracing_log::NormalizeEvent;
14 use tracing_subscriber::{
16 format::Writer, writer::BoxMakeWriter, FmtContext, FormatEvent, FormatFields,
17 FormattedFields, MakeWriter,
21 util::SubscriberInitExt,
24 use tracing_tree::HierarchicalLayer;
26 pub(crate) struct Logger {
31 struct MakeWriterStderr;
33 impl<'a> MakeWriter<'a> for MakeWriterStderr {
36 fn make_writer(&'a self) -> Self::Writer {
42 pub(crate) fn new(file: Option<File>, filter: Option<&str>) -> Logger {
43 let filter = filter.map_or(EnvFilter::default(), EnvFilter::new);
45 Logger { filter, file }
48 pub(crate) fn install(self) -> Result<()> {
49 // The meaning of CHALK_DEBUG I suspected is to tell chalk crates
50 // (i.e. chalk-solve, chalk-ir, chalk-recursive) how to filter tracing
51 // logs. But now we can only have just one filter, which means we have to
52 // merge chalk filter to our main filter (from RA_LOG env).
54 // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`.
55 // As the value should only affect chalk crates, we'd better manually
56 // specify the target. And for simplicity, CHALK_DEBUG only accept the value
57 // that specify level.
58 let chalk_level_dir = std::env::var("CHALK_DEBUG")
60 val.parse::<LevelFilter>().expect(
61 "invalid CHALK_DEBUG value, expect right log level (like debug or trace)",
66 let chalk_layer = HierarchicalLayer::default()
67 .with_indent_lines(true)
69 .with_indent_amount(2)
70 .with_writer(io::stderr);
72 let writer = match self.file {
73 Some(file) => BoxMakeWriter::new(Arc::new(file)),
74 None => BoxMakeWriter::new(io::stderr),
77 tracing_subscriber::fmt::layer().event_format(LoggerFormatter).with_writer(writer);
79 match chalk_level_dir {
84 .add_directive(format!("chalk_solve={}", val).parse()?)
85 .add_directive(format!("chalk_ir={}", val).parse()?)
86 .add_directive(format!("chalk_recursive={}", val).parse()?),
93 Registry::default().with(self.filter).with(ra_fmt_layer).init();
102 struct LoggerFormatter;
104 impl<S, N> FormatEvent<S, N> for LoggerFormatter
106 S: Subscriber + for<'a> LookupSpan<'a>,
107 N: for<'a> FormatFields<'a> + 'static,
111 ctx: &FmtContext<'_, S, N>,
112 mut writer: Writer<'_>,
115 // Write level and target
116 let level = *event.metadata().level();
118 // If this event is issued from `log` crate, then the value of target is
119 // always "log". `tracing-log` has hard coded it for some reason, so we
120 // need to extract it using `normalized_metadata` method which is part of
121 // `tracing_log::NormalizeEvent`.
122 let target = match event.normalized_metadata() {
123 // This event is issued from `log` crate
124 Some(log) => log.target(),
125 None => event.metadata().target(),
127 write!(writer, "[{} {}] ", level, target)?;
129 // Write spans and fields of each span
130 ctx.visit_spans(|span| {
131 write!(writer, "{}", span.name())?;
133 let ext = span.extensions();
135 // `FormattedFields` is a a formatted representation of the span's
136 // fields, which is stored in its extensions by the `fmt` layer's
137 // `new_span` method. The fields will have been formatted
138 // by the same field formatter that's provided to the event
139 // formatter in the `FmtContext`.
140 let fields = &ext.get::<FormattedFields<N>>().expect("will never be `None`");
142 if !fields.is_empty() {
143 write!(writer, "{{{}}}", fields)?;
145 write!(writer, ": ")?;
150 // Write fields on the event
151 ctx.field_format().format_fields(writer.by_ref(), event)?;