#![doc(html_root_url = "https://docs.rs/tracing-log/0.1.2")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
)]
#![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub,
bad_style,
const_err,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
private_in_public,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
use lazy_static::lazy_static;
use std::{fmt, io};
use tracing_core::{
callsite::{self, Callsite},
dispatcher,
field::{self, Field, Visit},
identify_callsite,
metadata::{Kind, Level},
subscriber, Event, Metadata,
};
#[cfg(feature = "log-tracer")]
#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
pub mod log_tracer;
#[cfg(feature = "trace-logger")]
#[cfg_attr(docsrs, doc(cfg(feature = "trace-logger")))]
pub mod trace_logger;
#[cfg(feature = "log-tracer")]
#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
#[doc(inline)]
pub use self::log_tracer::LogTracer;
#[cfg(feature = "trace-logger")]
#[cfg_attr(docsrs, doc(cfg(feature = "trace-logger")))]
#[deprecated(
since = "0.1.1",
note = "use the `tracing` crate's \"log\" feature flag instead"
)]
#[allow(deprecated)]
#[doc(inline)]
pub use self::trace_logger::TraceLogger;
#[cfg(feature = "env_logger")]
#[cfg_attr(docsrs, doc(cfg(feature = "env_logger")))]
pub mod env_logger;
pub use log;
pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
dispatch_record(record);
Ok(())
}
pub(crate) fn dispatch_record(record: &log::Record<'_>) {
dispatcher::get_default(|dispatch| {
let filter_meta = record.as_trace();
if !dispatch.enabled(&filter_meta) {
return;
}
let (_, keys, meta) = loglevel_to_cs(record.level());
let log_module = record.module_path();
let log_file = record.file();
let log_line = record.line();
let module = log_module.as_ref().map(|s| s as &dyn field::Value);
let file = log_file.as_ref().map(|s| s as &dyn field::Value);
let line = log_line.as_ref().map(|s| s as &dyn field::Value);
dispatch.event(&Event::new(
meta,
&meta.fields().value_set(&[
(&keys.message, Some(record.args() as &dyn field::Value)),
(&keys.target, Some(&record.target())),
(&keys.module, module),
(&keys.file, file),
(&keys.line, line),
]),
));
});
}
pub trait AsLog: crate::sealed::Sealed {
type Log;
fn as_log(&self) -> Self::Log;
}
pub trait AsTrace: crate::sealed::Sealed {
type Trace;
fn as_trace(&self) -> Self::Trace;
}
impl<'a> crate::sealed::Sealed for Metadata<'a> {}
impl<'a> AsLog for Metadata<'a> {
type Log = log::Metadata<'a>;
fn as_log(&self) -> Self::Log {
log::Metadata::builder()
.level(self.level().as_log())
.target(self.target())
.build()
}
}
impl<'a> crate::sealed::Sealed for log::Metadata<'a> {}
impl<'a> AsTrace for log::Metadata<'a> {
type Trace = Metadata<'a>;
fn as_trace(&self) -> Self::Trace {
let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
Metadata::new(
"log record",
self.target(),
self.level().as_trace(),
None,
None,
None,
field::FieldSet::new(FIELD_NAMES, cs_id),
Kind::EVENT,
)
}
}
struct Fields {
message: field::Field,
target: field::Field,
module: field::Field,
file: field::Field,
line: field::Field,
}
static FIELD_NAMES: &[&str] = &[
"message",
"log.target",
"log.module_path",
"log.file",
"log.line",
];
impl Fields {
fn new(cs: &'static dyn Callsite) -> Self {
let fieldset = cs.metadata().fields();
let message = fieldset.field("message").unwrap();
let target = fieldset.field("log.target").unwrap();
let module = fieldset.field("log.module_path").unwrap();
let file = fieldset.field("log.file").unwrap();
let line = fieldset.field("log.line").unwrap();
Fields {
message,
target,
module,
file,
line,
}
}
}
macro_rules! log_cs {
($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
struct $ty;
static $cs: $ty = $ty;
static $meta: Metadata<'static> = Metadata::new(
"log event",
"log",
$level,
None,
None,
None,
field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
Kind::EVENT,
);
impl callsite::Callsite for $ty {
fn set_interest(&self, _: subscriber::Interest) {}
fn metadata(&self) -> &'static Metadata<'static> {
&$meta
}
}
};
}
log_cs!(
tracing_core::Level::TRACE,
TRACE_CS,
TRACE_META,
TraceCallsite
);
log_cs!(
tracing_core::Level::DEBUG,
DEBUG_CS,
DEBUG_META,
DebugCallsite
);
log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
log_cs!(
tracing_core::Level::ERROR,
ERROR_CS,
ERROR_META,
ErrorCallsite
);
lazy_static! {
static ref TRACE_FIELDS: Fields = Fields::new(&TRACE_CS);
static ref DEBUG_FIELDS: Fields = Fields::new(&DEBUG_CS);
static ref INFO_FIELDS: Fields = Fields::new(&INFO_CS);
static ref WARN_FIELDS: Fields = Fields::new(&WARN_CS);
static ref ERROR_FIELDS: Fields = Fields::new(&ERROR_CS);
}
fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
match level {
Level::TRACE => (&TRACE_CS, &*TRACE_FIELDS),
Level::DEBUG => (&DEBUG_CS, &*DEBUG_FIELDS),
Level::INFO => (&INFO_CS, &*INFO_FIELDS),
Level::WARN => (&WARN_CS, &*WARN_FIELDS),
Level::ERROR => (&ERROR_CS, &*ERROR_FIELDS),
}
}
fn loglevel_to_cs(
level: log::Level,
) -> (
&'static dyn Callsite,
&'static Fields,
&'static Metadata<'static>,
) {
match level {
log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
}
}
impl<'a> crate::sealed::Sealed for log::Record<'a> {}
impl<'a> AsTrace for log::Record<'a> {
type Trace = Metadata<'a>;
fn as_trace(&self) -> Self::Trace {
let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
Metadata::new(
"log record",
self.target(),
self.level().as_trace(),
self.file(),
self.line(),
self.module_path(),
field::FieldSet::new(FIELD_NAMES, cs_id),
Kind::EVENT,
)
}
}
impl crate::sealed::Sealed for tracing_core::Level {}
impl AsLog for tracing_core::Level {
type Log = log::Level;
fn as_log(&self) -> log::Level {
match *self {
tracing_core::Level::ERROR => log::Level::Error,
tracing_core::Level::WARN => log::Level::Warn,
tracing_core::Level::INFO => log::Level::Info,
tracing_core::Level::DEBUG => log::Level::Debug,
tracing_core::Level::TRACE => log::Level::Trace,
}
}
}
impl crate::sealed::Sealed for log::Level {}
impl AsTrace for log::Level {
type Trace = tracing_core::Level;
#[inline]
fn as_trace(&self) -> tracing_core::Level {
match self {
log::Level::Error => tracing_core::Level::ERROR,
log::Level::Warn => tracing_core::Level::WARN,
log::Level::Info => tracing_core::Level::INFO,
log::Level::Debug => tracing_core::Level::DEBUG,
log::Level::Trace => tracing_core::Level::TRACE,
}
}
}
impl crate::sealed::Sealed for log::LevelFilter {}
impl AsTrace for log::LevelFilter {
type Trace = tracing_core::LevelFilter;
#[inline]
fn as_trace(&self) -> tracing_core::LevelFilter {
match self {
log::LevelFilter::Off => tracing_core::LevelFilter::OFF,
log::LevelFilter::Error => tracing_core::LevelFilter::ERROR,
log::LevelFilter::Warn => tracing_core::LevelFilter::WARN,
log::LevelFilter::Info => tracing_core::LevelFilter::INFO,
log::LevelFilter::Debug => tracing_core::LevelFilter::DEBUG,
log::LevelFilter::Trace => tracing_core::LevelFilter::TRACE,
}
}
}
impl crate::sealed::Sealed for tracing_core::LevelFilter {}
impl AsLog for tracing_core::LevelFilter {
type Log = log::LevelFilter;
#[inline]
fn as_log(&self) -> Self::Log {
match *self {
tracing_core::LevelFilter::OFF => log::LevelFilter::Off,
tracing_core::LevelFilter::ERROR => log::LevelFilter::Error,
tracing_core::LevelFilter::WARN => log::LevelFilter::Warn,
tracing_core::LevelFilter::INFO => log::LevelFilter::Info,
tracing_core::LevelFilter::DEBUG => log::LevelFilter::Debug,
tracing_core::LevelFilter::TRACE => log::LevelFilter::Trace,
}
}
}
pub trait NormalizeEvent<'a>: crate::sealed::Sealed {
fn normalized_metadata(&'a self) -> Option<Metadata<'a>>;
fn is_log(&self) -> bool;
}
impl<'a> crate::sealed::Sealed for Event<'a> {}
impl<'a> NormalizeEvent<'a> for Event<'a> {
fn normalized_metadata(&'a self) -> Option<Metadata<'a>> {
let original = self.metadata();
if self.is_log() {
let mut fields = LogVisitor::new_for(self, level_to_cs(*original.level()).1);
self.record(&mut fields);
Some(Metadata::new(
"log event",
fields.target.unwrap_or("log"),
*original.level(),
fields.file,
fields.line.map(|l| l as u32),
fields.module_path,
field::FieldSet::new(&["message"], original.callsite()),
Kind::EVENT,
))
} else {
None
}
}
fn is_log(&self) -> bool {
self.metadata().callsite() == identify_callsite!(level_to_cs(*self.metadata().level()).0)
}
}
struct LogVisitor<'a> {
target: Option<&'a str>,
module_path: Option<&'a str>,
file: Option<&'a str>,
line: Option<u64>,
fields: &'static Fields,
}
impl<'a> LogVisitor<'a> {
fn new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self {
Self {
target: None,
module_path: None,
file: None,
line: None,
fields,
}
}
}
impl<'a> Visit for LogVisitor<'a> {
fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
fn record_u64(&mut self, field: &Field, value: u64) {
if field == &self.fields.line {
self.line = Some(value);
}
}
fn record_str(&mut self, field: &Field, value: &str) {
unsafe {
if field == &self.fields.file {
self.file = Some(&*(value as *const _));
} else if field == &self.fields.target {
self.target = Some(&*(value as *const _));
} else if field == &self.fields.module {
self.module_path = Some(&*(value as *const _));
}
}
}
}
mod sealed {
pub trait Sealed {}
}
#[cfg(test)]
mod test {
use super::*;
fn test_callsite(level: log::Level) {
let record = log::Record::builder()
.args(format_args!("Error!"))
.level(level)
.target("myApp")
.file(Some("server.rs"))
.line(Some(144))
.module_path(Some("server"))
.build();
let meta = record.as_trace();
let (cs, _keys, _) = loglevel_to_cs(record.level());
let cs_meta = cs.metadata();
assert_eq!(
meta.callsite(),
cs_meta.callsite(),
"actual: {:#?}\nexpected: {:#?}",
meta,
cs_meta
);
assert_eq!(meta.level(), &level.as_trace());
}
#[test]
fn error_callsite_is_correct() {
test_callsite(log::Level::Error);
}
#[test]
fn warn_callsite_is_correct() {
test_callsite(log::Level::Warn);
}
#[test]
fn info_callsite_is_correct() {
test_callsite(log::Level::Info);
}
#[test]
fn debug_callsite_is_correct() {
test_callsite(log::Level::Debug);
}
#[test]
fn trace_callsite_is_correct() {
test_callsite(log::Level::Trace);
}
}