]> git.proxmox.com Git - rustc.git/blame - vendor/tracing-log/src/lib.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / vendor / tracing-log / src / lib.rs
CommitLineData
f035d41b
XL
1//! Adapters for connecting unstructured log records from the `log` crate into
2//! the `tracing` ecosystem.
3//!
4//! # Overview
5//!
6//! [`tracing`] is a framework for instrumenting Rust programs with context-aware,
7//! structured, event-based diagnostic information. This crate provides
8//! compatibility layers for using `tracing` alongside the logging facade provided
9//! by the [`log`] crate.
10//!
11//! This crate provides:
12//!
13//! - [`AsTrace`] and [`AsLog`] traits for converting between `tracing` and `log` types.
14//! - [`LogTracer`], a [`log::Log`] implementation that consumes [`log::Record`]s
15//! and outputs them as [`tracing::Event`].
16//! - An [`env_logger`] module, with helpers for using the [`env_logger` crate]
17//! with `tracing` (optional, enabled by the `env-logger` feature).
18//!
6a06907d
XL
19//! *Compiler support: [requires `rustc` 1.42+][msrv]*
20//!
21//! [msrv]: #supported-rust-versions
22//!
f035d41b
XL
23//! # Usage
24//!
25//! ## Convert log records to tracing `Event`s
26//!
27//! To convert [`log::Record`]s as [`tracing::Event`]s, set `LogTracer` as the default
28//! logger by calling its [`init`] or [`init_with_filter`] methods.
29//!
30//! ```rust
31//! # use std::error::Error;
32//! use tracing_log::LogTracer;
33//! use log;
34//!
35//! # fn main() -> Result<(), Box<Error>> {
36//! LogTracer::init()?;
37//!
38//! // will be available for Subscribers as a tracing Event
39//! log::trace!("an example trace log");
40//! # Ok(())
41//! # }
42//! ```
43//!
44//! This conversion does not convert unstructured data in log records (such as
45//! values passed as format arguments to the `log!` macro) to structured
46//! `tracing` fields. However, it *does* attach these new events to to the
47//! span that was currently executing when the record was logged. This is the
48//! primary use-case for this library: making it possible to locate the log
49//! records emitted by dependencies which use `log` within the context of a
50//! trace.
51//!
52//! ## Convert tracing `Event`s to logs
53//!
54//! Enabling the ["log" and "log-always" feature flags][flags] on the `tracing`
55//! crate will cause all `tracing` spans and events to emit `log::Record`s as
56//! they occur.
57//!
58//! ## Caution: Mixing both conversions
59//!
60//! Note that logger implementations that convert log records to trace events
61//! should not be used with `Subscriber`s that convert trace events _back_ into
62//! log records (such as the `TraceLogger`), as doing so will result in the
63//! event recursing between the subscriber and the logger forever (or, in real
64//! life, probably overflowing the call stack).
65//!
66//! If the logging of trace events generated from log records produced by the
67//! `log` crate is desired, either the `log` crate should not be used to
68//! implement this logging, or an additional layer of filtering will be
69//! required to avoid infinitely converting between `Event` and `log::Record`.
70//!
71//! # Feature Flags
72//! * `trace-logger`: enables an experimental `log` subscriber, deprecated since
73//! version 0.1.1.
74//! * `log-tracer`: enables the `LogTracer` type (on by default)
75//! * `env_logger`: enables the `env_logger` module, with helpers for working
76//! with the [`env_logger` crate].
77//!
6a06907d
XL
78//! ## Supported Rust Versions
79//!
80//! Tracing is built against the latest stable release. The minimum supported
81//! version is 1.42. The current Tracing version is not guaranteed to build on
82//! Rust versions earlier than the minimum supported version.
83//!
84//! Tracing follows the same compiler support policies as the rest of the Tokio
85//! project. The current stable Rust compiler and the three most recent minor
86//! versions before it will always be supported. For example, if the current
87//! stable compiler version is 1.45, the minimum supported version will not be
88//! increased past 1.42, three minor versions prior. Increasing the minimum
89//! supported compiler version is not considered a semver breaking change as
90//! long as doing so complies with this policy.
91//!
f035d41b
XL
92//! [`init`]: struct.LogTracer.html#method.init
93//! [`init_with_filter`]: struct.LogTracer.html#method.init_with_filter
94//! [`AsTrace`]: trait.AsTrace.html
95//! [`AsLog`]: trait.AsLog.html
96//! [`LogTracer`]: struct.LogTracer.html
97//! [`TraceLogger`]: struct.TraceLogger.html
98//! [`env_logger`]: env_logger/index.html
99//! [`tracing`]: https://crates.io/crates/tracing
100//! [`log`]: https://crates.io/crates/log
101//! [`env_logger` crate]: https://crates.io/crates/env-logger
102//! [`log::Log`]: https://docs.rs/log/latest/log/trait.Log.html
103//! [`log::Record`]: https://docs.rs/log/latest/log/struct.Record.html
104//! [`tracing::Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
105//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
106//! [`tracing::Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html
6a06907d
XL
107//! [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags
108#![doc(html_root_url = "https://docs.rs/tracing-log/0.1.2")]
109#![doc(
110 html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
111 issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
112)]
113#![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))]
f035d41b
XL
114#![warn(
115 missing_debug_implementations,
116 missing_docs,
117 rust_2018_idioms,
118 unreachable_pub,
119 bad_style,
120 const_err,
121 dead_code,
122 improper_ctypes,
f035d41b
XL
123 non_shorthand_field_patterns,
124 no_mangle_generic_items,
125 overflowing_literals,
126 path_statements,
127 patterns_in_fns_without_body,
f035d41b 128 private_in_public,
f035d41b
XL
129 unconditional_recursion,
130 unused,
131 unused_allocation,
132 unused_comparisons,
133 unused_parens,
134 while_true
135)]
136use lazy_static::lazy_static;
137
138use std::{fmt, io};
139
140use tracing_core::{
141 callsite::{self, Callsite},
142 dispatcher,
143 field::{self, Field, Visit},
144 identify_callsite,
145 metadata::{Kind, Level},
146 subscriber, Event, Metadata,
147};
148
149#[cfg(feature = "log-tracer")]
6a06907d 150#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
f035d41b
XL
151pub mod log_tracer;
152
153#[cfg(feature = "trace-logger")]
6a06907d 154#[cfg_attr(docsrs, doc(cfg(feature = "trace-logger")))]
f035d41b
XL
155pub mod trace_logger;
156
157#[cfg(feature = "log-tracer")]
6a06907d 158#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
f035d41b
XL
159#[doc(inline)]
160pub use self::log_tracer::LogTracer;
161
162#[cfg(feature = "trace-logger")]
6a06907d 163#[cfg_attr(docsrs, doc(cfg(feature = "trace-logger")))]
f035d41b
XL
164#[deprecated(
165 since = "0.1.1",
166 note = "use the `tracing` crate's \"log\" feature flag instead"
167)]
168#[allow(deprecated)]
169#[doc(inline)]
170pub use self::trace_logger::TraceLogger;
171
172#[cfg(feature = "env_logger")]
6a06907d 173#[cfg_attr(docsrs, doc(cfg(feature = "env_logger")))]
f035d41b
XL
174pub mod env_logger;
175
6a06907d
XL
176pub use log;
177
f035d41b
XL
178/// Format a log record as a trace event in the current span.
179pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
6a06907d 180 dispatch_record(record);
f035d41b
XL
181 Ok(())
182}
183
6a06907d
XL
184// XXX(eliza): this is factored out so that we don't have to deal with the pub
185// function `format_trace`'s `Result` return type...maybe we should get rid of
186// that in 0.2...
187pub(crate) fn dispatch_record(record: &log::Record<'_>) {
188 dispatcher::get_default(|dispatch| {
189 let filter_meta = record.as_trace();
190 if !dispatch.enabled(&filter_meta) {
191 return;
192 }
193
194 let (_, keys, meta) = loglevel_to_cs(record.level());
195
196 let log_module = record.module_path();
197 let log_file = record.file();
198 let log_line = record.line();
199
200 let module = log_module.as_ref().map(|s| s as &dyn field::Value);
201 let file = log_file.as_ref().map(|s| s as &dyn field::Value);
202 let line = log_line.as_ref().map(|s| s as &dyn field::Value);
203
204 dispatch.event(&Event::new(
205 meta,
206 &meta.fields().value_set(&[
207 (&keys.message, Some(record.args() as &dyn field::Value)),
208 (&keys.target, Some(&record.target())),
209 (&keys.module, module),
210 (&keys.file, file),
211 (&keys.line, line),
212 ]),
213 ));
214 });
215}
216
f035d41b
XL
217/// Trait implemented for `tracing` types that can be converted to a `log`
218/// equivalent.
219pub trait AsLog: crate::sealed::Sealed {
220 /// The `log` type that this type can be converted into.
221 type Log;
222 /// Returns the `log` equivalent of `self`.
223 fn as_log(&self) -> Self::Log;
224}
225
226/// Trait implemented for `log` types that can be converted to a `tracing`
227/// equivalent.
228pub trait AsTrace: crate::sealed::Sealed {
229 /// The `tracing` type that this type can be converted into.
230 type Trace;
231 /// Returns the `tracing` equivalent of `self`.
232 fn as_trace(&self) -> Self::Trace;
233}
234
235impl<'a> crate::sealed::Sealed for Metadata<'a> {}
236
237impl<'a> AsLog for Metadata<'a> {
238 type Log = log::Metadata<'a>;
239 fn as_log(&self) -> Self::Log {
240 log::Metadata::builder()
241 .level(self.level().as_log())
242 .target(self.target())
243 .build()
244 }
245}
6a06907d
XL
246impl<'a> crate::sealed::Sealed for log::Metadata<'a> {}
247
248impl<'a> AsTrace for log::Metadata<'a> {
249 type Trace = Metadata<'a>;
250 fn as_trace(&self) -> Self::Trace {
251 let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
252 Metadata::new(
253 "log record",
254 self.target(),
255 self.level().as_trace(),
256 None,
257 None,
258 None,
259 field::FieldSet::new(FIELD_NAMES, cs_id),
260 Kind::EVENT,
261 )
262 }
263}
f035d41b
XL
264
265struct Fields {
266 message: field::Field,
267 target: field::Field,
268 module: field::Field,
269 file: field::Field,
270 line: field::Field,
271}
272
6a06907d 273static FIELD_NAMES: &[&str] = &[
f035d41b
XL
274 "message",
275 "log.target",
276 "log.module_path",
277 "log.file",
278 "log.line",
279];
280
281impl Fields {
282 fn new(cs: &'static dyn Callsite) -> Self {
283 let fieldset = cs.metadata().fields();
284 let message = fieldset.field("message").unwrap();
285 let target = fieldset.field("log.target").unwrap();
286 let module = fieldset.field("log.module_path").unwrap();
287 let file = fieldset.field("log.file").unwrap();
288 let line = fieldset.field("log.line").unwrap();
289 Fields {
290 message,
291 target,
292 module,
293 file,
294 line,
295 }
296 }
297}
298
299macro_rules! log_cs {
6a06907d
XL
300 ($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
301 struct $ty;
302 static $cs: $ty = $ty;
303 static $meta: Metadata<'static> = Metadata::new(
f035d41b
XL
304 "log event",
305 "log",
306 $level,
307 None,
308 None,
309 None,
6a06907d 310 field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
f035d41b
XL
311 Kind::EVENT,
312 );
313
6a06907d 314 impl callsite::Callsite for $ty {
f035d41b
XL
315 fn set_interest(&self, _: subscriber::Interest) {}
316 fn metadata(&self) -> &'static Metadata<'static> {
6a06907d 317 &$meta
f035d41b
XL
318 }
319 }
6a06907d 320 };
f035d41b
XL
321}
322
6a06907d
XL
323log_cs!(
324 tracing_core::Level::TRACE,
325 TRACE_CS,
326 TRACE_META,
327 TraceCallsite
328);
329log_cs!(
330 tracing_core::Level::DEBUG,
331 DEBUG_CS,
332 DEBUG_META,
333 DebugCallsite
334);
335log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
336log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
337log_cs!(
338 tracing_core::Level::ERROR,
339 ERROR_CS,
340 ERROR_META,
341 ErrorCallsite
342);
f035d41b
XL
343
344lazy_static! {
6a06907d
XL
345 static ref TRACE_FIELDS: Fields = Fields::new(&TRACE_CS);
346 static ref DEBUG_FIELDS: Fields = Fields::new(&DEBUG_CS);
347 static ref INFO_FIELDS: Fields = Fields::new(&INFO_CS);
348 static ref WARN_FIELDS: Fields = Fields::new(&WARN_CS);
349 static ref ERROR_FIELDS: Fields = Fields::new(&ERROR_CS);
f035d41b
XL
350}
351
6a06907d
XL
352fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
353 match level {
354 Level::TRACE => (&TRACE_CS, &*TRACE_FIELDS),
355 Level::DEBUG => (&DEBUG_CS, &*DEBUG_FIELDS),
356 Level::INFO => (&INFO_CS, &*INFO_FIELDS),
357 Level::WARN => (&WARN_CS, &*WARN_FIELDS),
358 Level::ERROR => (&ERROR_CS, &*ERROR_FIELDS),
f035d41b
XL
359 }
360}
361
6a06907d
XL
362fn loglevel_to_cs(
363 level: log::Level,
364) -> (
365 &'static dyn Callsite,
366 &'static Fields,
367 &'static Metadata<'static>,
368) {
f035d41b 369 match level {
6a06907d
XL
370 log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
371 log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
372 log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
373 log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
374 log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
f035d41b
XL
375 }
376}
377
378impl<'a> crate::sealed::Sealed for log::Record<'a> {}
379
380impl<'a> AsTrace for log::Record<'a> {
381 type Trace = Metadata<'a>;
382 fn as_trace(&self) -> Self::Trace {
383 let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
384 Metadata::new(
385 "log record",
386 self.target(),
387 self.level().as_trace(),
388 self.file(),
389 self.line(),
390 self.module_path(),
391 field::FieldSet::new(FIELD_NAMES, cs_id),
392 Kind::EVENT,
393 )
394 }
395}
396
397impl crate::sealed::Sealed for tracing_core::Level {}
398
399impl AsLog for tracing_core::Level {
400 type Log = log::Level;
401 fn as_log(&self) -> log::Level {
402 match *self {
403 tracing_core::Level::ERROR => log::Level::Error,
404 tracing_core::Level::WARN => log::Level::Warn,
405 tracing_core::Level::INFO => log::Level::Info,
406 tracing_core::Level::DEBUG => log::Level::Debug,
407 tracing_core::Level::TRACE => log::Level::Trace,
408 }
409 }
410}
411
412impl crate::sealed::Sealed for log::Level {}
413
414impl AsTrace for log::Level {
415 type Trace = tracing_core::Level;
6a06907d 416 #[inline]
f035d41b
XL
417 fn as_trace(&self) -> tracing_core::Level {
418 match self {
419 log::Level::Error => tracing_core::Level::ERROR,
420 log::Level::Warn => tracing_core::Level::WARN,
421 log::Level::Info => tracing_core::Level::INFO,
422 log::Level::Debug => tracing_core::Level::DEBUG,
423 log::Level::Trace => tracing_core::Level::TRACE,
424 }
425 }
426}
427
6a06907d
XL
428impl crate::sealed::Sealed for log::LevelFilter {}
429
430impl AsTrace for log::LevelFilter {
431 type Trace = tracing_core::LevelFilter;
432 #[inline]
433 fn as_trace(&self) -> tracing_core::LevelFilter {
434 match self {
435 log::LevelFilter::Off => tracing_core::LevelFilter::OFF,
436 log::LevelFilter::Error => tracing_core::LevelFilter::ERROR,
437 log::LevelFilter::Warn => tracing_core::LevelFilter::WARN,
438 log::LevelFilter::Info => tracing_core::LevelFilter::INFO,
439 log::LevelFilter::Debug => tracing_core::LevelFilter::DEBUG,
440 log::LevelFilter::Trace => tracing_core::LevelFilter::TRACE,
441 }
442 }
443}
444
445impl crate::sealed::Sealed for tracing_core::LevelFilter {}
446
447impl AsLog for tracing_core::LevelFilter {
448 type Log = log::LevelFilter;
449 #[inline]
450 fn as_log(&self) -> Self::Log {
451 match *self {
452 tracing_core::LevelFilter::OFF => log::LevelFilter::Off,
453 tracing_core::LevelFilter::ERROR => log::LevelFilter::Error,
454 tracing_core::LevelFilter::WARN => log::LevelFilter::Warn,
455 tracing_core::LevelFilter::INFO => log::LevelFilter::Info,
456 tracing_core::LevelFilter::DEBUG => log::LevelFilter::Debug,
457 tracing_core::LevelFilter::TRACE => log::LevelFilter::Trace,
458 }
459 }
460}
f035d41b
XL
461/// Extends log `Event`s to provide complete `Metadata`.
462///
463/// In `tracing-log`, an `Event` produced by a log (through [`AsTrace`]) has an hard coded
464/// "log" target and no `file`, `line`, or `module_path` attributes. This happens because `Event`
465/// requires its `Metadata` to be `'static`, while [`log::Record`]s provide them with a generic
466/// lifetime.
467///
468/// However, these values are stored in the `Event`'s fields and
469/// the [`normalized_metadata`] method allows to build a new `Metadata`
470/// that only lives as long as its source `Event`, but provides complete
471/// data.
472///
473/// It can typically be used by `Subscriber`s when processing an `Event`,
474/// to allow accessing its complete metadata in a consistent way,
475/// regardless of the source of its source.
476///
477/// [`normalized_metadata`]: trait.NormalizeEvent.html#normalized_metadata
478/// [`AsTrace`]: trait.AsTrace.html
479/// [`log::Record`]: https://docs.rs/log/0.4.7/log/struct.Record.html
480pub trait NormalizeEvent<'a>: crate::sealed::Sealed {
481 /// If this `Event` comes from a `log`, this method provides a new
482 /// normalized `Metadata` which has all available attributes
483 /// from the original log, including `file`, `line`, `module_path`
484 /// and `target`.
485 /// Returns `None` is the `Event` is not issued from a `log`.
486 fn normalized_metadata(&'a self) -> Option<Metadata<'a>>;
487 /// Returns whether this `Event` represents a log (from the `log` crate)
488 fn is_log(&self) -> bool;
489}
490
491impl<'a> crate::sealed::Sealed for Event<'a> {}
492
493impl<'a> NormalizeEvent<'a> for Event<'a> {
494 fn normalized_metadata(&'a self) -> Option<Metadata<'a>> {
495 let original = self.metadata();
496 if self.is_log() {
6a06907d 497 let mut fields = LogVisitor::new_for(self, level_to_cs(*original.level()).1);
f035d41b
XL
498 self.record(&mut fields);
499
500 Some(Metadata::new(
501 "log event",
502 fields.target.unwrap_or("log"),
6a06907d 503 *original.level(),
f035d41b
XL
504 fields.file,
505 fields.line.map(|l| l as u32),
506 fields.module_path,
507 field::FieldSet::new(&["message"], original.callsite()),
508 Kind::EVENT,
509 ))
510 } else {
511 None
512 }
513 }
514
515 fn is_log(&self) -> bool {
6a06907d 516 self.metadata().callsite() == identify_callsite!(level_to_cs(*self.metadata().level()).0)
f035d41b
XL
517 }
518}
519
520struct LogVisitor<'a> {
521 target: Option<&'a str>,
522 module_path: Option<&'a str>,
523 file: Option<&'a str>,
524 line: Option<u64>,
525 fields: &'static Fields,
526}
527
528impl<'a> LogVisitor<'a> {
529 // We don't actually _use_ the provided event argument; it is simply to
530 // ensure that the `LogVisitor` does not outlive the event whose fields it
531 // is visiting, so that the reference casts in `record_str` are safe.
532 fn new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self {
533 Self {
534 target: None,
535 module_path: None,
536 file: None,
537 line: None,
538 fields,
539 }
540 }
541}
542
543impl<'a> Visit for LogVisitor<'a> {
544 fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
545
546 fn record_u64(&mut self, field: &Field, value: u64) {
547 if field == &self.fields.line {
548 self.line = Some(value);
549 }
550 }
551
552 fn record_str(&mut self, field: &Field, value: &str) {
553 unsafe {
554 // The `Visit` API erases the string slice's lifetime. However, we
555 // know it is part of the `Event` struct with a lifetime of `'a`. If
556 // (and only if!) this `LogVisitor` was constructed with the same
557 // lifetime parameter `'a` as the event in question, it's safe to
558 // cast these string slices to the `'a` lifetime.
559 if field == &self.fields.file {
560 self.file = Some(&*(value as *const _));
561 } else if field == &self.fields.target {
562 self.target = Some(&*(value as *const _));
563 } else if field == &self.fields.module {
564 self.module_path = Some(&*(value as *const _));
565 }
566 }
567 }
568}
569
570mod sealed {
571 pub trait Sealed {}
572}
573
574#[cfg(test)]
575mod test {
576 use super::*;
577
578 fn test_callsite(level: log::Level) {
579 let record = log::Record::builder()
580 .args(format_args!("Error!"))
581 .level(level)
582 .target("myApp")
583 .file(Some("server.rs"))
584 .line(Some(144))
585 .module_path(Some("server"))
586 .build();
587
588 let meta = record.as_trace();
6a06907d 589 let (cs, _keys, _) = loglevel_to_cs(record.level());
f035d41b
XL
590 let cs_meta = cs.metadata();
591 assert_eq!(
592 meta.callsite(),
593 cs_meta.callsite(),
594 "actual: {:#?}\nexpected: {:#?}",
595 meta,
596 cs_meta
597 );
598 assert_eq!(meta.level(), &level.as_trace());
599 }
600
601 #[test]
602 fn error_callsite_is_correct() {
603 test_callsite(log::Level::Error);
604 }
605
606 #[test]
607 fn warn_callsite_is_correct() {
608 test_callsite(log::Level::Warn);
609 }
610
611 #[test]
612 fn info_callsite_is_correct() {
613 test_callsite(log::Level::Info);
614 }
615
616 #[test]
617 fn debug_callsite_is_correct() {
618 test_callsite(log::Level::Debug);
619 }
620
621 #[test]
622 fn trace_callsite_is_correct() {
623 test_callsite(log::Level::Trace);
624 }
625}