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. 3 4 use std::{ 5 fmt, 6 fs::File, 7 io::{self, Stderr}, 8 sync::Arc, 9 }; 10 11 use rust_analyzer::Result; 12 use tracing::{level_filters::LevelFilter, Event, Subscriber}; 13 use tracing_log::NormalizeEvent; 14 use tracing_subscriber::{ 15 fmt::{ 16 format::Writer, writer::BoxMakeWriter, FmtContext, FormatEvent, FormatFields, 17 FormattedFields, MakeWriter, 18 }, 19 layer::SubscriberExt, 20 registry::LookupSpan, 21 util::SubscriberInitExt, 22 EnvFilter, Registry, 23 }; 24 use tracing_tree::HierarchicalLayer; 25 26 pub(crate) struct Logger { 27 filter: EnvFilter, 28 file: Option<File>, 29 } 30 31 struct MakeWriterStderr; 32 33 impl<'a> MakeWriter<'a> for MakeWriterStderr { 34 type Writer = Stderr; 35 make_writer(&'a self) -> Self::Writer36 fn make_writer(&'a self) -> Self::Writer { 37 io::stderr() 38 } 39 } 40 41 impl Logger { new(file: Option<File>, filter: Option<&str>) -> Logger42 pub(crate) fn new(file: Option<File>, filter: Option<&str>) -> Logger { 43 let filter = filter.map_or(EnvFilter::default(), EnvFilter::new); 44 45 Logger { filter, file } 46 } 47 install(self) -> Result<()>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). 53 // 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 mannually 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") 59 .map(|val| { 60 val.parse::<LevelFilter>().expect( 61 "invalid CHALK_DEBUG value, expect right log level (like debug or trace)", 62 ) 63 }) 64 .ok(); 65 66 let chalk_layer = HierarchicalLayer::default() 67 .with_indent_lines(true) 68 .with_ansi(false) 69 .with_indent_amount(2) 70 .with_writer(io::stderr); 71 72 let writer = match self.file { 73 Some(file) => BoxMakeWriter::new(Arc::new(file)), 74 None => BoxMakeWriter::new(io::stderr), 75 }; 76 let ra_fmt_layer = 77 tracing_subscriber::fmt::layer().event_format(LoggerFormatter).with_writer(writer); 78 79 match chalk_level_dir { 80 Some(val) => { 81 Registry::default() 82 .with( 83 self.filter 84 .add_directive(format!("chalk_solve={}", val).parse()?) 85 .add_directive(format!("chalk_ir={}", val).parse()?) 86 .add_directive(format!("chalk_recursive={}", val).parse()?), 87 ) 88 .with(ra_fmt_layer) 89 .with(chalk_layer) 90 .init(); 91 } 92 None => { 93 Registry::default().with(self.filter).with(ra_fmt_layer).init(); 94 } 95 }; 96 97 Ok(()) 98 } 99 } 100 101 #[derive(Debug)] 102 struct LoggerFormatter; 103 104 impl<S, N> FormatEvent<S, N> for LoggerFormatter 105 where 106 S: Subscriber + for<'a> LookupSpan<'a>, 107 N: for<'a> FormatFields<'a> + 'static, 108 { format_event( &self, ctx: &FmtContext<'_, S, N>, mut writer: Writer, event: &Event<'_>, ) -> fmt::Result109 fn format_event( 110 &self, 111 ctx: &FmtContext<'_, S, N>, 112 mut writer: Writer, 113 event: &Event<'_>, 114 ) -> fmt::Result { 115 // Write level and target 116 let level = *event.metadata().level(); 117 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(), 126 }; 127 write!(writer, "[{} {}] ", level, target)?; 128 129 // Write spans and fields of each span 130 ctx.visit_spans(|span| { 131 write!(writer, "{}", span.name())?; 132 133 let ext = span.extensions(); 134 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`"); 141 142 if !fields.is_empty() { 143 write!(writer, "{{{}}}", fields)?; 144 } 145 write!(writer, ": ")?; 146 147 Ok(()) 148 })?; 149 150 // Write fields on the event 151 ctx.field_format().format_fields(writer.by_ref(), event)?; 152 153 writeln!(writer) 154 } 155 } 156