Skip to content

Commit 0cdd5e8

Browse files
committed
log: forward LogTracer::enabled to the subscriber (#1254)
PRs #1247, #1248, and #1251 improve `tracing`'s behavior for the `log` crate's `log_enabled!` macro when a max-level hint is set. However, this *doesn't* get us correct behavior when a particular target is not enabled but the level is permitted by the max-level filter. In this case, `log_enabled!` will still return `true` when using `LogTracer`, because it doesn't currently call `Subscriber::enabled` on the current subscriber in its `Log::enabled` method. Instead, `Subscriber::enabled` is only checked when actually recording an event. This means that when a target is disabled by a target-specific filter but it's below the max level, `log::log_enabled!` will erroneously return `true`. This also means that skipping disabled `log` records in similar cases will construct the `log::Record` struct even when it isn't necessary to do so. This PR improves this behavior by adding a call to `Subscriber::enabled` in `LogTracer`'s `Log::enabled` method. I've also added to the existing tests for filtering `log` records to ensure that we also handle the `log_enabled!` macro correctly. While I was here, I noticed that the `Log::log` method for `LogTracer` is somewhat inefficient --- it gets the current dispatcher *three* times, and checks `enabled` twice. Currently, we check if the event would be enabled, and then call the`format_trace` function, which *also* checks if the event is enabled, and then dispatches it if it is. This is not great. :/ I fixed this by moving the check-and-dispatch inside of a single `dispatcher::get_default` block, and removing the duplicate check. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
1 parent 8d83326 commit 0cdd5e8

File tree

4 files changed

+172
-109
lines changed

4 files changed

+172
-109
lines changed

tracing-log/src/lib.rs

Lines changed: 103 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -177,35 +177,43 @@ pub use log;
177177

178178
/// Format a log record as a trace event in the current span.
179179
pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
180-
let filter_meta = record.as_trace();
181-
if !dispatcher::get_default(|dispatch| dispatch.enabled(&filter_meta)) {
182-
return Ok(());
183-
};
184-
185-
let (cs, keys) = loglevel_to_cs(record.level());
186-
187-
let log_module = record.module_path();
188-
let log_file = record.file();
189-
let log_line = record.line();
190-
191-
let module = log_module.as_ref().map(|s| s as &dyn field::Value);
192-
let file = log_file.as_ref().map(|s| s as &dyn field::Value);
193-
let line = log_line.as_ref().map(|s| s as &dyn field::Value);
194-
195-
let meta = cs.metadata();
196-
Event::dispatch(
197-
&meta,
198-
&meta.fields().value_set(&[
199-
(&keys.message, Some(record.args() as &dyn field::Value)),
200-
(&keys.target, Some(&record.target())),
201-
(&keys.module, module),
202-
(&keys.file, file),
203-
(&keys.line, line),
204-
]),
205-
);
180+
dispatch_record(record);
206181
Ok(())
207182
}
208183

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...
187+
pub(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+
209217
/// Trait implemented for `tracing` types that can be converted to a `log`
210218
/// equivalent.
211219
pub trait AsLog: crate::sealed::Sealed {
@@ -235,6 +243,24 @@ impl<'a> AsLog for Metadata<'a> {
235243
.build()
236244
}
237245
}
246+
impl<'a> crate::sealed::Sealed for log::Metadata<'a> {}
247+
248+
impl<'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+
}
238264

239265
struct Fields {
240266
message: field::Field,
@@ -271,62 +297,81 @@ impl Fields {
271297
}
272298

273299
macro_rules! log_cs {
274-
($level:expr) => {{
275-
struct Callsite;
276-
static CALLSITE: Callsite = Callsite;
277-
static META: Metadata<'static> = Metadata::new(
300+
($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
301+
struct $ty;
302+
static $cs: $ty = $ty;
303+
static $meta: Metadata<'static> = Metadata::new(
278304
"log event",
279305
"log",
280306
$level,
281307
None,
282308
None,
283309
None,
284-
field::FieldSet::new(FIELD_NAMES, identify_callsite!(&CALLSITE)),
310+
field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
285311
Kind::EVENT,
286312
);
287313

288-
impl callsite::Callsite for Callsite {
314+
impl callsite::Callsite for $ty {
289315
fn set_interest(&self, _: subscriber::Interest) {}
290316
fn metadata(&self) -> &'static Metadata<'static> {
291-
&META
317+
&$meta
292318
}
293319
}
294-
295-
&CALLSITE
296-
}};
320+
};
297321
}
298322

299-
static TRACE_CS: &'static dyn Callsite = log_cs!(tracing_core::Level::TRACE);
300-
static DEBUG_CS: &'static dyn Callsite = log_cs!(tracing_core::Level::DEBUG);
301-
static INFO_CS: &'static dyn Callsite = log_cs!(tracing_core::Level::INFO);
302-
static WARN_CS: &'static dyn Callsite = log_cs!(tracing_core::Level::WARN);
303-
static ERROR_CS: &'static dyn Callsite = log_cs!(tracing_core::Level::ERROR);
323+
log_cs!(
324+
tracing_core::Level::TRACE,
325+
TRACE_CS,
326+
TRACE_META,
327+
TraceCallsite
328+
);
329+
log_cs!(
330+
tracing_core::Level::DEBUG,
331+
DEBUG_CS,
332+
DEBUG_META,
333+
DebugCallsite
334+
);
335+
log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
336+
log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
337+
log_cs!(
338+
tracing_core::Level::ERROR,
339+
ERROR_CS,
340+
ERROR_META,
341+
ErrorCallsite
342+
);
304343

305344
lazy_static! {
306-
static ref TRACE_FIELDS: Fields = Fields::new(TRACE_CS);
307-
static ref DEBUG_FIELDS: Fields = Fields::new(DEBUG_CS);
308-
static ref INFO_FIELDS: Fields = Fields::new(INFO_CS);
309-
static ref WARN_FIELDS: Fields = Fields::new(WARN_CS);
310-
static ref ERROR_FIELDS: Fields = Fields::new(ERROR_CS);
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);
311350
}
312351

313352
fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
314353
match level {
315-
Level::TRACE => (TRACE_CS, &*TRACE_FIELDS),
316-
Level::DEBUG => (DEBUG_CS, &*DEBUG_FIELDS),
317-
Level::INFO => (INFO_CS, &*INFO_FIELDS),
318-
Level::WARN => (WARN_CS, &*WARN_FIELDS),
319-
Level::ERROR => (ERROR_CS, &*ERROR_FIELDS),
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),
320359
}
321360
}
322361

323-
fn loglevel_to_cs(level: log::Level) -> (&'static dyn Callsite, &'static Fields) {
362+
fn loglevel_to_cs(
363+
level: log::Level,
364+
) -> (
365+
&'static dyn Callsite,
366+
&'static Fields,
367+
&'static Metadata<'static>,
368+
) {
324369
match level {
325-
log::Level::Trace => (TRACE_CS, &*TRACE_FIELDS),
326-
log::Level::Debug => (DEBUG_CS, &*DEBUG_FIELDS),
327-
log::Level::Info => (INFO_CS, &*INFO_FIELDS),
328-
log::Level::Warn => (WARN_CS, &*WARN_FIELDS),
329-
log::Level::Error => (ERROR_CS, &*ERROR_FIELDS),
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),
330375
}
331376
}
332377

@@ -541,7 +586,7 @@ mod test {
541586
.build();
542587

543588
let meta = record.as_trace();
544-
let (cs, _keys) = loglevel_to_cs(record.level());
589+
let (cs, _keys, _) = loglevel_to_cs(record.level());
545590
let cs_meta = cs.metadata();
546591
assert_eq!(
547592
meta.callsite(),

tracing-log/src/log_tracer.rs

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@
1919
//! such as when a crate emits both `tracing` diagnostics _and_ log records by
2020
//! default.
2121
//!
22-
//! [`LogTracer`]: struct.LogTracer.html
23-
//! [`log`]: https://docs.rs/log/0.4.8/log/
24-
//! [logger interface]: https://docs.rs/log/0.4.8/log/trait.Log.html
25-
//! [`init`]: struct.LogTracer.html#method.init.html
26-
//! [`init_with_filter`]: struct.LogTracer.html#method.init_with_filter.html
27-
//! [builder]: struct.LogTracer.html#method.builder
28-
//! [ignore]: struct.Builder.html#method.ignore_crate
29-
use crate::{format_trace, AsTrace};
22+
//! [logger interface]: log::Log
23+
//! [`init`]: LogTracer.html#method.init
24+
//! [`init_with_filter`]: LogTracer.html#method.init_with_filter
25+
//! [builder]: LogTracer::builder()
26+
//! [ignore]: Builder::ignore_crate()
27+
use crate::AsTrace;
3028
pub use log::SetLoggerError;
3129
use tracing_core::dispatcher;
3230

@@ -169,34 +167,23 @@ impl log::Log for LogTracer {
169167

170168
// Okay, it wasn't disabled by the max level — do we have any specific
171169
// modules to ignore?
172-
if self.ignore_crates.is_empty() {
173-
// If we don't, just enable it.
174-
return true;
170+
if !self.ignore_crates.is_empty() {
171+
// If we are ignoring certain module paths, ensure that the metadata
172+
// does not start with one of those paths.
173+
let target = metadata.target();
174+
for ignored in &self.ignore_crates[..] {
175+
if target.starts_with(ignored) {
176+
return false;
177+
}
178+
}
175179
}
176180

177-
// If we are ignoring certain module paths, ensure that the metadata
178-
// does not start with one of those paths.
179-
let target = metadata.target();
180-
!self
181-
.ignore_crates
182-
.iter()
183-
.any(|ignored| target.starts_with(ignored))
181+
// Finally, check if the current `tracing` dispatcher cares about this.
182+
dispatcher::get_default(|dispatch| dispatch.enabled(&metadata.as_trace()))
184183
}
185184

186185
fn log(&self, record: &log::Record<'_>) {
187-
let enabled = dispatcher::get_default(|dispatch| {
188-
// TODO: can we cache this for each log record, so we can get
189-
// similar to the callsite cache?
190-
dispatch.enabled(&record.as_trace())
191-
});
192-
193-
if enabled {
194-
// TODO: if the record is enabled, we'll get the current dispatcher
195-
// twice --- once to check if enabled, and again to dispatch the event.
196-
// If we could construct events without dispatching them, we could
197-
// re-use the dispatcher reference...
198-
format_trace(record).unwrap();
199-
}
186+
crate::dispatch_record(record);
200187
}
201188

202189
fn flush(&self) {}

tracing-log/tests/log_tracer.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ struct OwnedMetadata {
2121
struct TestSubscriber(Arc<State>);
2222

2323
impl Subscriber for TestSubscriber {
24-
fn enabled(&self, _: &Metadata<'_>) -> bool {
24+
fn enabled(&self, meta: &Metadata<'_>) -> bool {
25+
dbg!(meta);
2526
true
2627
}
2728

@@ -34,6 +35,7 @@ impl Subscriber for TestSubscriber {
3435
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
3536

3637
fn event(&self, event: &Event<'_>) {
38+
dbg!(event);
3739
*self.0.last_normalized_metadata.lock().unwrap() = (
3840
event.is_log(),
3941
event.normalized_metadata().map(|normalized| OwnedMetadata {
@@ -107,7 +109,9 @@ fn normalized_metadata() {
107109
}
108110

109111
fn last(state: &State, should_be_log: bool, expected: Option<OwnedMetadata>) {
110-
let metadata = state.last_normalized_metadata.lock().unwrap();
111-
assert_eq!(metadata.0, should_be_log);
112-
assert_eq!(metadata.1, expected);
112+
let lock = state.last_normalized_metadata.lock().unwrap();
113+
let (is_log, metadata) = &*lock;
114+
dbg!(&metadata);
115+
assert_eq!(dbg!(*is_log), should_be_log);
116+
assert_eq!(metadata.as_ref(), expected.as_ref());
113117
}
Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
mod support;
22
use self::support::*;
3-
use tracing::{self, subscriber::with_default, Level};
3+
use tracing::{self, Level};
44
use tracing_subscriber::{filter::EnvFilter, prelude::*};
55

6+
mod my_module {
7+
pub(crate) fn test_records() {
8+
log::trace!("this should be disabled");
9+
log::info!("this shouldn't be");
10+
log::debug!("this should be disabled");
11+
log::warn!("this should be enabled");
12+
log::warn!(target: "something else", "this shouldn't be enabled");
13+
log::error!("this should be enabled too");
14+
}
15+
16+
pub(crate) fn test_log_enabled() {
17+
assert!(
18+
log::log_enabled!(log::Level::Info),
19+
"info should be enabled inside `my_module`"
20+
);
21+
assert!(
22+
!log::log_enabled!(log::Level::Debug),
23+
"debug should not be enabled inside `my_module`"
24+
);
25+
assert!(
26+
log::log_enabled!(log::Level::Warn),
27+
"warn should be enabled inside `my_module`"
28+
);
29+
}
30+
}
31+
632
#[test]
733
fn log_is_enabled() {
8-
mod my_module {
9-
pub(crate) fn do_test() {
10-
log::trace!("this should be disabled");
11-
log::info!("this shouldn't be");
12-
log::debug!("this should be disabled");
13-
log::warn!("this should be enabled");
14-
log::warn!(target: "something else", "this shouldn't be enabled");
15-
log::error!("this should be enabled too");
16-
}
17-
}
18-
tracing_log::LogTracer::init().expect("logger should be unset");
1934
let filter: EnvFilter = "filter_log::my_module=info"
2035
.parse()
2136
.expect("filter should parse");
@@ -25,11 +40,23 @@ fn log_is_enabled() {
2540
.event(event::mock().at_level(Level::ERROR))
2641
.done()
2742
.run_with_handle();
28-
let subscriber = subscriber.with(filter);
2943

30-
with_default(subscriber, || {
31-
my_module::do_test();
32-
});
44+
// Note: we have to set the global default in order to set the `log` max
45+
// level, which can only be set once.
46+
subscriber.with(filter).init();
47+
48+
my_module::test_records();
49+
log::info!("this is disabled");
50+
51+
my_module::test_log_enabled();
52+
assert!(
53+
!log::log_enabled!(log::Level::Info),
54+
"info should not be enabled outside `my_module`"
55+
);
56+
assert!(
57+
!log::log_enabled!(log::Level::Warn),
58+
"warn should not be enabled outside `my_module`"
59+
);
3360

3461
finished.assert_finished();
3562
}

0 commit comments

Comments
 (0)