1#![doc(
100 html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
101 html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico",
102 issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
103)]
104#![cfg_attr(docsrs, feature(doc_cfg))]
105#![warn(
106 missing_debug_implementations,
107 missing_docs,
108 rust_2018_idioms,
109 unreachable_pub,
110 bad_style,
111 dead_code,
112 improper_ctypes,
113 non_shorthand_field_patterns,
114 no_mangle_generic_items,
115 overflowing_literals,
116 path_statements,
117 patterns_in_fns_without_body,
118 private_interfaces,
119 private_bounds,
120 unconditional_recursion,
121 unused,
122 unused_allocation,
123 unused_comparisons,
124 unused_parens,
125 while_true
126)]
127use once_cell::sync::Lazy;
128
129use std::{fmt, io};
130
131use tracing_core::{
132 callsite::{self, Callsite},
133 collect, dispatch,
134 field::{self, Field, Visit},
135 identify_callsite,
136 metadata::{Kind, Level},
137 Event, Metadata,
138};
139
140#[cfg(feature = "log-tracer")]
141#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
142pub mod log_tracer;
143
144#[cfg(feature = "log-tracer")]
145#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
146#[doc(inline)]
147pub use self::log_tracer::LogTracer;
148
149#[cfg(feature = "env_logger")]
150#[cfg_attr(docsrs, doc(cfg(feature = "env_logger")))]
151pub mod env_logger;
152
153pub use log;
154
155pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
157 dispatch_record(record);
158 Ok(())
159}
160
161pub(crate) fn dispatch_record(record: &log::Record<'_>) {
165 dispatch::get_default(|dispatch| {
166 let filter_meta = record.as_trace();
167 if !dispatch.enabled(&filter_meta) {
168 return;
169 }
170
171 let (_, keys, meta) = loglevel_to_cs(record.level());
172
173 let log_module = record.module_path();
174 let log_file = record.file();
175 let log_line = record.line();
176
177 let module = log_module.as_ref().map(|s| s as &dyn field::Value);
178 let file = log_file.as_ref().map(|s| s as &dyn field::Value);
179 let line = log_line.as_ref().map(|s| s as &dyn field::Value);
180
181 dispatch.event(&Event::new(
182 meta,
183 &meta.fields().value_set(&[
184 (&keys.message, Some(record.args() as &dyn field::Value)),
185 (&keys.target, Some(&record.target())),
186 (&keys.module, module),
187 (&keys.file, file),
188 (&keys.line, line),
189 ]),
190 ));
191 });
192}
193
194pub trait AsLog: crate::sealed::Sealed {
197 type Log;
199 fn as_log(&self) -> Self::Log;
201}
202
203pub trait AsTrace: crate::sealed::Sealed {
206 type Trace;
208 fn as_trace(&self) -> Self::Trace;
210}
211
212impl crate::sealed::Sealed for Metadata<'_> {}
213
214impl<'a> AsLog for Metadata<'a> {
215 type Log = log::Metadata<'a>;
216 fn as_log(&self) -> Self::Log {
217 log::Metadata::builder()
218 .level(self.level().as_log())
219 .target(self.target())
220 .build()
221 }
222}
223impl crate::sealed::Sealed for log::Metadata<'_> {}
224
225impl<'a> AsTrace for log::Metadata<'a> {
226 type Trace = Metadata<'a>;
227 fn as_trace(&self) -> Self::Trace {
228 let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
229 Metadata::new(
230 "log record",
231 self.target(),
232 self.level().as_trace(),
233 None,
234 None,
235 None,
236 field::FieldSet::new(FIELD_NAMES, cs_id),
237 Kind::EVENT,
238 )
239 }
240}
241
242struct Fields {
243 message: field::Field,
244 target: field::Field,
245 module: field::Field,
246 file: field::Field,
247 line: field::Field,
248}
249
250static FIELD_NAMES: &[&str] = &[
251 "message",
252 "log.target",
253 "log.module_path",
254 "log.file",
255 "log.line",
256];
257
258impl Fields {
259 fn new(cs: &'static dyn Callsite) -> Self {
260 let fieldset = cs.metadata().fields();
261 let message = fieldset.field("message").unwrap();
262 let target = fieldset.field("log.target").unwrap();
263 let module = fieldset.field("log.module_path").unwrap();
264 let file = fieldset.field("log.file").unwrap();
265 let line = fieldset.field("log.line").unwrap();
266 Fields {
267 message,
268 target,
269 module,
270 file,
271 line,
272 }
273 }
274}
275
276macro_rules! log_cs {
277 ($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
278 struct $ty;
279 static $cs: $ty = $ty;
280 static $meta: Metadata<'static> = Metadata::new(
281 "log event",
282 "log",
283 $level,
284 ::core::option::Option::None,
285 ::core::option::Option::None,
286 ::core::option::Option::None,
287 field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
288 Kind::EVENT,
289 );
290
291 impl callsite::Callsite for $ty {
292 fn set_interest(&self, _: collect::Interest) {}
293 fn metadata(&self) -> &'static Metadata<'static> {
294 &$meta
295 }
296 }
297 };
298}
299
300log_cs!(
301 tracing_core::Level::TRACE,
302 TRACE_CS,
303 TRACE_META,
304 TraceCallsite
305);
306log_cs!(
307 tracing_core::Level::DEBUG,
308 DEBUG_CS,
309 DEBUG_META,
310 DebugCallsite
311);
312log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
313log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
314log_cs!(
315 tracing_core::Level::ERROR,
316 ERROR_CS,
317 ERROR_META,
318 ErrorCallsite
319);
320
321static TRACE_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&TRACE_CS));
322static DEBUG_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&DEBUG_CS));
323static INFO_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&INFO_CS));
324static WARN_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&WARN_CS));
325static ERROR_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&ERROR_CS));
326
327fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
328 match level {
329 Level::TRACE => (&TRACE_CS, &*TRACE_FIELDS),
330 Level::DEBUG => (&DEBUG_CS, &*DEBUG_FIELDS),
331 Level::INFO => (&INFO_CS, &*INFO_FIELDS),
332 Level::WARN => (&WARN_CS, &*WARN_FIELDS),
333 Level::ERROR => (&ERROR_CS, &*ERROR_FIELDS),
334 }
335}
336
337fn loglevel_to_cs(
338 level: log::Level,
339) -> (
340 &'static dyn Callsite,
341 &'static Fields,
342 &'static Metadata<'static>,
343) {
344 match level {
345 log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
346 log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
347 log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
348 log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
349 log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
350 }
351}
352
353impl crate::sealed::Sealed for log::Record<'_> {}
354
355impl<'a> AsTrace for log::Record<'a> {
356 type Trace = Metadata<'a>;
357 fn as_trace(&self) -> Self::Trace {
358 let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
359 Metadata::new(
360 "log record",
361 self.target(),
362 self.level().as_trace(),
363 self.file(),
364 self.line(),
365 self.module_path(),
366 field::FieldSet::new(FIELD_NAMES, cs_id),
367 Kind::EVENT,
368 )
369 }
370}
371
372impl crate::sealed::Sealed for tracing_core::Level {}
373
374impl AsLog for tracing_core::Level {
375 type Log = log::Level;
376 fn as_log(&self) -> log::Level {
377 match *self {
378 tracing_core::Level::ERROR => log::Level::Error,
379 tracing_core::Level::WARN => log::Level::Warn,
380 tracing_core::Level::INFO => log::Level::Info,
381 tracing_core::Level::DEBUG => log::Level::Debug,
382 tracing_core::Level::TRACE => log::Level::Trace,
383 }
384 }
385}
386
387impl crate::sealed::Sealed for log::Level {}
388
389impl AsTrace for log::Level {
390 type Trace = tracing_core::Level;
391 #[inline]
392 fn as_trace(&self) -> tracing_core::Level {
393 match self {
394 log::Level::Error => tracing_core::Level::ERROR,
395 log::Level::Warn => tracing_core::Level::WARN,
396 log::Level::Info => tracing_core::Level::INFO,
397 log::Level::Debug => tracing_core::Level::DEBUG,
398 log::Level::Trace => tracing_core::Level::TRACE,
399 }
400 }
401}
402
403impl crate::sealed::Sealed for log::LevelFilter {}
404
405impl AsTrace for log::LevelFilter {
406 type Trace = tracing_core::LevelFilter;
407 #[inline]
408 fn as_trace(&self) -> tracing_core::LevelFilter {
409 match self {
410 log::LevelFilter::Off => tracing_core::LevelFilter::OFF,
411 log::LevelFilter::Error => tracing_core::LevelFilter::ERROR,
412 log::LevelFilter::Warn => tracing_core::LevelFilter::WARN,
413 log::LevelFilter::Info => tracing_core::LevelFilter::INFO,
414 log::LevelFilter::Debug => tracing_core::LevelFilter::DEBUG,
415 log::LevelFilter::Trace => tracing_core::LevelFilter::TRACE,
416 }
417 }
418}
419
420impl crate::sealed::Sealed for tracing_core::LevelFilter {}
421
422impl AsLog for tracing_core::LevelFilter {
423 type Log = log::LevelFilter;
424 #[inline]
425 fn as_log(&self) -> Self::Log {
426 match *self {
427 tracing_core::LevelFilter::OFF => log::LevelFilter::Off,
428 tracing_core::LevelFilter::ERROR => log::LevelFilter::Error,
429 tracing_core::LevelFilter::WARN => log::LevelFilter::Warn,
430 tracing_core::LevelFilter::INFO => log::LevelFilter::Info,
431 tracing_core::LevelFilter::DEBUG => log::LevelFilter::Debug,
432 tracing_core::LevelFilter::TRACE => log::LevelFilter::Trace,
433 }
434 }
435}
436pub trait NormalizeEvent<'a>: crate::sealed::Sealed {
454 fn normalized_metadata(&'a self) -> Option<Metadata<'a>>;
460 fn is_log(&self) -> bool;
462}
463
464impl crate::sealed::Sealed for Event<'_> {}
465
466impl<'a> NormalizeEvent<'a> for Event<'a> {
467 fn normalized_metadata(&'a self) -> Option<Metadata<'a>> {
468 let original = self.metadata();
469 if self.is_log() {
470 let mut fields = LogVisitor::new_for(self, level_to_cs(*original.level()).1);
471 self.record(&mut fields);
472
473 Some(Metadata::new(
474 "log event",
475 fields.target.unwrap_or("log"),
476 *original.level(),
477 fields.file,
478 fields.line.map(|l| l as u32),
479 fields.module_path,
480 field::FieldSet::new(&["message"], original.callsite()),
481 Kind::EVENT,
482 ))
483 } else {
484 None
485 }
486 }
487
488 fn is_log(&self) -> bool {
489 self.metadata().callsite() == identify_callsite!(level_to_cs(*self.metadata().level()).0)
490 }
491}
492
493struct LogVisitor<'a> {
494 target: Option<&'a str>,
495 module_path: Option<&'a str>,
496 file: Option<&'a str>,
497 line: Option<u64>,
498 fields: &'static Fields,
499}
500
501impl<'a> LogVisitor<'a> {
502 fn new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self {
506 Self {
507 target: None,
508 module_path: None,
509 file: None,
510 line: None,
511 fields,
512 }
513 }
514}
515
516impl Visit for LogVisitor<'_> {
517 fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
518
519 fn record_u64(&mut self, field: &Field, value: u64) {
520 if field == &self.fields.line {
521 self.line = Some(value);
522 }
523 }
524
525 fn record_str(&mut self, field: &Field, value: &str) {
526 unsafe {
527 if field == &self.fields.file {
533 self.file = Some(&*(value as *const _));
534 } else if field == &self.fields.target {
535 self.target = Some(&*(value as *const _));
536 } else if field == &self.fields.module {
537 self.module_path = Some(&*(value as *const _));
538 }
539 }
540 }
541}
542
543mod sealed {
544 pub trait Sealed {}
545}
546
547#[cfg(test)]
548mod test {
549 use super::*;
550
551 fn test_callsite(level: log::Level) {
552 let record = log::Record::builder()
553 .args(format_args!("Error!"))
554 .level(level)
555 .target("myApp")
556 .file(Some("server.rs"))
557 .line(Some(144))
558 .module_path(Some("server"))
559 .build();
560
561 let meta = record.as_trace();
562 let (cs, _keys, _) = loglevel_to_cs(record.level());
563 let cs_meta = cs.metadata();
564 assert_eq!(
565 meta.callsite(),
566 cs_meta.callsite(),
567 "actual: {:#?}\nexpected: {:#?}",
568 meta,
569 cs_meta
570 );
571 assert_eq!(meta.level(), &level.as_trace());
572 }
573
574 #[test]
575 fn error_callsite_is_correct() {
576 test_callsite(log::Level::Error);
577 }
578
579 #[test]
580 fn warn_callsite_is_correct() {
581 test_callsite(log::Level::Warn);
582 }
583
584 #[test]
585 fn info_callsite_is_correct() {
586 test_callsite(log::Level::Info);
587 }
588
589 #[test]
590 fn debug_callsite_is_correct() {
591 test_callsite(log::Level::Debug);
592 }
593
594 #[test]
595 fn trace_callsite_is_correct() {
596 test_callsite(log::Level::Trace);
597 }
598}