1 //! A `tracing` [`Subscriber`] that uses the [`log`] crate as a backend for 2 //! formatting `tracing` spans and events. 3 //! 4 //! When a [`TraceLogger`] is set as the current subscriber, it will record 5 //! traces by emitting [`log::Record`]s that can be collected by a logger. 6 //! 7 //! **Note**: This API has been deprecated since version 0.1.1. In order to emit 8 //! `tracing` events as `log` records, the ["log" and "log-always" feature 9 //! flags][flags] on the `tracing` crate should be used instead. 10 //! 11 //! [`log`]: log 12 //! [`Subscriber`]: https://docs.rs/tracing/0.1.7/tracing/subscriber/trait.Subscriber.html 13 //! [`log::Record`]:log::Record 14 //! [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags 15 #![deprecated( 16 since = "0.1.1", 17 note = "use the `tracing` crate's \"log\" feature flag instead" 18 )] 19 use crate::AsLog; 20 use std::{ 21 cell::RefCell, 22 collections::HashMap, 23 fmt::{self, Write}, 24 sync::{ 25 atomic::{AtomicUsize, Ordering}, 26 Mutex, 27 }, 28 }; 29 use tracing_core::{ 30 field, 31 span::{self, Id}, 32 Event, Metadata, Subscriber, 33 }; 34 35 /// A `tracing` [`Subscriber`] implementation that logs all recorded 36 /// trace events. 37 /// 38 /// **Note**: This API has been deprecated since version 0.1.1. In order to emit 39 /// `tracing` events as `log` records, the ["log" and "log-always" feature 40 /// flags][flags] on the `tracing` crate should be used instead. 41 /// 42 /// [`Subscriber`]: https://docs.rs/tracing/0.1.7/tracing/subscriber/trait.Subscriber.html 43 /// [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags 44 pub struct TraceLogger { 45 settings: Builder, 46 spans: Mutex<HashMap<Id, SpanLineBuilder>>, 47 next_id: AtomicUsize, 48 } 49 50 thread_local! { 51 static CURRENT: RefCell<Vec<Id>> = RefCell::new(Vec::new()); 52 } 53 /// Configures and constructs a [`TraceLogger`]. 54 /// 55 #[derive(Debug)] 56 pub struct Builder { 57 log_span_closes: bool, 58 log_enters: bool, 59 log_exits: bool, 60 log_ids: bool, 61 parent_fields: bool, 62 log_parent: bool, 63 } 64 65 // ===== impl TraceLogger ===== 66 67 impl TraceLogger { 68 /// Returns a new `TraceLogger` with the default configuration. new() -> Self69 pub fn new() -> Self { 70 Self::builder().finish() 71 } 72 73 /// Returns a `Builder` for configuring a `TraceLogger`. builder() -> Builder74 pub fn builder() -> Builder { 75 Default::default() 76 } 77 from_builder(settings: Builder) -> Self78 fn from_builder(settings: Builder) -> Self { 79 Self { 80 settings, 81 ..Default::default() 82 } 83 } 84 next_id(&self) -> Id85 fn next_id(&self) -> Id { 86 Id::from_u64(self.next_id.fetch_add(1, Ordering::SeqCst) as u64) 87 } 88 } 89 90 // ===== impl Builder ===== 91 92 impl Builder { 93 /// Configures whether or not the [`TraceLogger`] being constructed will log 94 /// when a span closes. 95 /// with_span_closes(self, log_span_closes: bool) -> Self96 pub fn with_span_closes(self, log_span_closes: bool) -> Self { 97 Self { 98 log_span_closes, 99 ..self 100 } 101 } 102 103 /// Configures whether or not the [`TraceLogger`] being constructed will 104 /// include the fields of parent spans when formatting events. 105 /// with_parent_fields(self, parent_fields: bool) -> Self106 pub fn with_parent_fields(self, parent_fields: bool) -> Self { 107 Self { 108 parent_fields, 109 ..self 110 } 111 } 112 113 /// Configures whether or not the [`TraceLogger`] being constructed will log 114 /// when a span is entered. 115 /// 116 /// If this is set to false, fields from the current span will still be 117 /// recorded as context, but the actual entry will not create a log record. 118 /// with_span_entry(self, log_enters: bool) -> Self119 pub fn with_span_entry(self, log_enters: bool) -> Self { 120 Self { log_enters, ..self } 121 } 122 123 /// Configures whether or not the [`TraceLogger`] being constructed will log 124 /// when a span is exited. 125 /// with_span_exits(self, log_exits: bool) -> Self126 pub fn with_span_exits(self, log_exits: bool) -> Self { 127 Self { log_exits, ..self } 128 } 129 130 /// Configures whether or not the [`TraceLogger`] being constructed will 131 /// include span IDs when formatting log output. 132 /// with_ids(self, log_ids: bool) -> Self133 pub fn with_ids(self, log_ids: bool) -> Self { 134 Self { log_ids, ..self } 135 } 136 137 /// Configures whether or not the [`TraceLogger`] being constructed will 138 /// include the names of parent spans as context when formatting events. 139 /// with_parent_names(self, log_parent: bool) -> Self140 pub fn with_parent_names(self, log_parent: bool) -> Self { 141 Self { log_parent, ..self } 142 } 143 144 /// Complete the builder, returning a configured [`TraceLogger`]. 145 /// finish(self) -> TraceLogger146 pub fn finish(self) -> TraceLogger { 147 TraceLogger::from_builder(self) 148 } 149 } 150 151 impl Default for Builder { default() -> Self152 fn default() -> Self { 153 Builder { 154 log_span_closes: false, 155 parent_fields: true, 156 log_exits: false, 157 log_ids: false, 158 log_parent: true, 159 log_enters: false, 160 } 161 } 162 } 163 164 impl Default for TraceLogger { default() -> Self165 fn default() -> Self { 166 TraceLogger { 167 settings: Default::default(), 168 spans: Default::default(), 169 next_id: AtomicUsize::new(1), 170 } 171 } 172 } 173 174 #[derive(Debug)] 175 struct SpanLineBuilder { 176 parent: Option<Id>, 177 ref_count: usize, 178 fields: String, 179 file: Option<String>, 180 line: Option<u32>, 181 module_path: Option<String>, 182 target: String, 183 level: log::Level, 184 name: &'static str, 185 } 186 187 impl SpanLineBuilder { new(parent: Option<Id>, meta: &Metadata<'_>, fields: String) -> Self188 fn new(parent: Option<Id>, meta: &Metadata<'_>, fields: String) -> Self { 189 Self { 190 parent, 191 ref_count: 1, 192 fields, 193 file: meta.file().map(String::from), 194 line: meta.line(), 195 module_path: meta.module_path().map(String::from), 196 target: String::from(meta.target()), 197 level: meta.level().as_log(), 198 name: meta.name(), 199 } 200 } 201 log_meta(&self) -> log::Metadata<'_>202 fn log_meta(&self) -> log::Metadata<'_> { 203 log::MetadataBuilder::new() 204 .level(self.level) 205 .target(self.target.as_ref()) 206 .build() 207 } 208 finish(self)209 fn finish(self) { 210 let log_meta = self.log_meta(); 211 let logger = log::logger(); 212 if logger.enabled(&log_meta) { 213 logger.log( 214 &log::Record::builder() 215 .metadata(log_meta) 216 .target(self.target.as_ref()) 217 .module_path(self.module_path.as_ref().map(String::as_ref)) 218 .file(self.file.as_ref().map(String::as_ref)) 219 .line(self.line) 220 .args(format_args!("close {}; {}", self.name, self.fields)) 221 .build(), 222 ); 223 } 224 } 225 } 226 227 impl field::Visit for SpanLineBuilder { record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug)228 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { 229 write!(self.fields, " {}={:?};", field.name(), value) 230 .expect("write to string should never fail") 231 } 232 } 233 234 impl Subscriber for TraceLogger { enabled(&self, metadata: &Metadata<'_>) -> bool235 fn enabled(&self, metadata: &Metadata<'_>) -> bool { 236 log::logger().enabled(&metadata.as_log()) 237 } 238 new_span(&self, attrs: &span::Attributes<'_>) -> Id239 fn new_span(&self, attrs: &span::Attributes<'_>) -> Id { 240 let id = self.next_id(); 241 let mut spans = self.spans.lock().unwrap(); 242 let mut fields = String::new(); 243 let parent = self.current_id(); 244 if self.settings.parent_fields { 245 let mut next_parent = parent.as_ref(); 246 while let Some(parent) = next_parent.and_then(|p| spans.get(p)) { 247 write!(&mut fields, "{}", parent.fields).expect("write to string cannot fail"); 248 next_parent = parent.parent.as_ref(); 249 } 250 } 251 let mut span = SpanLineBuilder::new(parent, attrs.metadata(), fields); 252 attrs.record(&mut span); 253 spans.insert(id.clone(), span); 254 id 255 } 256 record(&self, span: &Id, values: &span::Record<'_>)257 fn record(&self, span: &Id, values: &span::Record<'_>) { 258 let mut spans = self.spans.lock().unwrap(); 259 if let Some(span) = spans.get_mut(span) { 260 values.record(span); 261 } 262 } 263 record_follows_from(&self, span: &Id, follows: &Id)264 fn record_follows_from(&self, span: &Id, follows: &Id) { 265 // TODO: this should eventually track the relationship? 266 log::logger().log( 267 &log::Record::builder() 268 .level(log::Level::Trace) 269 .args(format_args!("span {:?} follows_from={:?};", span, follows)) 270 .build(), 271 ); 272 } 273 enter(&self, id: &Id)274 fn enter(&self, id: &Id) { 275 let _ = CURRENT.try_with(|current| { 276 let mut current = current.borrow_mut(); 277 if current.contains(id) { 278 // Ignore duplicate enters. 279 return; 280 } 281 current.push(id.clone()); 282 }); 283 let spans = self.spans.lock().unwrap(); 284 if self.settings.log_enters { 285 if let Some(span) = spans.get(id) { 286 let log_meta = span.log_meta(); 287 let logger = log::logger(); 288 if logger.enabled(&log_meta) { 289 let current_id = self.current_id(); 290 let current_fields = current_id 291 .as_ref() 292 .and_then(|id| spans.get(id)) 293 .map(|span| span.fields.as_ref()) 294 .unwrap_or(""); 295 if self.settings.log_ids { 296 logger.log( 297 &log::Record::builder() 298 .metadata(log_meta) 299 .target(span.target.as_ref()) 300 .module_path(span.module_path.as_ref().map(String::as_ref)) 301 .file(span.file.as_ref().map(String::as_ref)) 302 .line(span.line) 303 .args(format_args!( 304 "enter {}; in={:?}; {}", 305 span.name, current_id, current_fields 306 )) 307 .build(), 308 ); 309 } else { 310 logger.log( 311 &log::Record::builder() 312 .metadata(log_meta) 313 .target(span.target.as_ref()) 314 .module_path(span.module_path.as_ref().map(String::as_ref)) 315 .file(span.file.as_ref().map(String::as_ref)) 316 .line(span.line) 317 .args(format_args!("enter {}; {}", span.name, current_fields)) 318 .build(), 319 ); 320 } 321 } 322 } 323 } 324 } 325 exit(&self, id: &Id)326 fn exit(&self, id: &Id) { 327 let _ = CURRENT.try_with(|current| { 328 let mut current = current.borrow_mut(); 329 if current.last() == Some(id) { 330 current.pop() 331 } else { 332 None 333 } 334 }); 335 if self.settings.log_exits { 336 let spans = self.spans.lock().unwrap(); 337 if let Some(span) = spans.get(id) { 338 let log_meta = span.log_meta(); 339 let logger = log::logger(); 340 if logger.enabled(&log_meta) { 341 logger.log( 342 &log::Record::builder() 343 .metadata(log_meta) 344 .target(span.target.as_ref()) 345 .module_path(span.module_path.as_ref().map(String::as_ref)) 346 .file(span.file.as_ref().map(String::as_ref)) 347 .line(span.line) 348 .args(format_args!("exit {}", span.name)) 349 .build(), 350 ); 351 } 352 } 353 } 354 } 355 event(&self, event: &Event<'_>)356 fn event(&self, event: &Event<'_>) { 357 let meta = event.metadata(); 358 let log_meta = meta.as_log(); 359 let logger = log::logger(); 360 if logger.enabled(&log_meta) { 361 let spans = self.spans.lock().unwrap(); 362 let current = self.current_id().and_then(|id| spans.get(&id)); 363 let (current_fields, parent) = current 364 .map(|span| { 365 let fields = span.fields.as_ref(); 366 let parent = if self.settings.log_parent { 367 Some(span.name) 368 } else { 369 None 370 }; 371 (fields, parent) 372 }) 373 .unwrap_or(("", None)); 374 logger.log( 375 &log::Record::builder() 376 .metadata(log_meta) 377 .target(meta.target()) 378 .module_path(meta.module_path().as_ref().cloned()) 379 .file(meta.file().as_ref().cloned()) 380 .line(meta.line()) 381 .args(format_args!( 382 "{}{}{}{}", 383 parent.unwrap_or(""), 384 if parent.is_some() { ": " } else { "" }, 385 LogEvent(event), 386 current_fields, 387 )) 388 .build(), 389 ); 390 } 391 } 392 clone_span(&self, id: &Id) -> Id393 fn clone_span(&self, id: &Id) -> Id { 394 let mut spans = self.spans.lock().unwrap(); 395 if let Some(span) = spans.get_mut(id) { 396 span.ref_count += 1; 397 } 398 id.clone() 399 } 400 try_close(&self, id: Id) -> bool401 fn try_close(&self, id: Id) -> bool { 402 let mut spans = self.spans.lock().unwrap(); 403 if spans.contains_key(&id) { 404 if spans.get(&id).unwrap().ref_count == 1 { 405 let span = spans.remove(&id).unwrap(); 406 if self.settings.log_span_closes { 407 span.finish(); 408 } 409 return true; 410 } else { 411 spans.get_mut(&id).unwrap().ref_count -= 1; 412 } 413 } 414 false 415 } 416 } 417 418 impl TraceLogger { 419 #[inline] current_id(&self) -> Option<Id>420 fn current_id(&self) -> Option<Id> { 421 CURRENT 422 .try_with(|current| current.borrow().last().map(|span| self.clone_span(span))) 423 .ok()? 424 } 425 } 426 427 struct LogEvent<'a>(&'a Event<'a>); 428 429 impl<'a> fmt::Display for LogEvent<'a> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result430 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 431 let mut has_logged = false; 432 let mut format_fields = |field: &field::Field, value: &dyn fmt::Debug| { 433 let name = field.name(); 434 let leading = if has_logged { " " } else { "" }; 435 // TODO: handle fmt error? 436 let _ = if name == "message" { 437 write!(f, "{}{:?};", leading, value) 438 } else { 439 write!(f, "{}{}={:?};", leading, name, value) 440 }; 441 has_logged = true; 442 }; 443 444 self.0.record(&mut format_fields); 445 Ok(()) 446 } 447 } 448 449 impl fmt::Debug for TraceLogger { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 451 f.debug_struct("TraceLogger") 452 .field("settings", &self.settings) 453 .field("spans", &self.spans) 454 .field("current", &self.current_id()) 455 .field("next_id", &self.next_id) 456 .finish() 457 } 458 } 459