Skip to content

Commit 71da8a5

Browse files
guswynnhawkwdavidbarsky
authored
tracing: add enabled! macro (#1821)
## Motivation Closes: #1668 My usecase is different than the referenced "avoid doing something expensive to log": I want to guard turning on `debug` mode for an ffi'd library, based on some `target` that represents the "module" we care about. ## Solution The macro is very similar to `event!`, but adds a few simplistic cases, and generates ever so slightly different code (to return the correct value always. It also skips anything to do with `tracing-log`. I considered (and tried), to share the impl between `event!` and `enabled!`, but must confess I am not good at macros and got stuck. I think they are sufficiently different, where copied impls, is easier to read. We already manage Also, my project is on the crates.io version, so this would need to be backported to 0.1, I can help with that with guidance. Co-authored-by: Eliza Weisman <eliza@buoyant.io> Co-authored-by: David Barsky <me@davidbarsky.com>
1 parent 820613c commit 71da8a5

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed

tracing-core/src/metadata.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ impl<'a> fmt::Debug for Metadata<'a> {
373373
enum KindInner {
374374
Event,
375375
Span,
376+
Hint,
376377
}
377378

378379
impl Kind {
@@ -382,6 +383,11 @@ impl Kind {
382383
/// `Span` callsite
383384
pub const SPAN: Kind = Kind(KindInner::Span);
384385

386+
/// `enabled!` callsite. [`Collect`][`crate::collect::Collect`]s can assume
387+
/// this `Kind` means they will never recieve a
388+
/// full event with this [`Metadata`].
389+
pub const HINT: Kind = Kind(KindInner::Hint);
390+
385391
/// Return true if the callsite kind is `Span`
386392
pub fn is_span(&self) -> bool {
387393
matches!(self, Kind(KindInner::Span))
@@ -391,6 +397,11 @@ impl Kind {
391397
pub fn is_event(&self) -> bool {
392398
matches!(self, Kind(KindInner::Event))
393399
}
400+
401+
/// Return true if the callsite kind is `Hint`
402+
pub fn is_hint(&self) -> bool {
403+
matches!(self, Kind(KindInner::Hint))
404+
}
394405
}
395406

396407
// ===== impl Level =====

tracing/src/macros.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,138 @@ macro_rules! event {
783783
);
784784
}
785785

786+
/// Checks whether a span or event is [enabled] based on the provided [metadata].
787+
///
788+
/// [enabled]: crate::Collect::enabled
789+
/// [metadata]: crate::Metadata
790+
///
791+
/// This macro is a specialized tool: it is intended to be used prior
792+
/// to an expensive computation required *just* for that event, but
793+
/// *cannot* be done as part of an argument to that event, such as
794+
/// when multiple events are emitted (e.g., iterating over a collection
795+
/// and emitting an event for each item).
796+
///
797+
/// # Usage
798+
///
799+
/// [Collectors] can make filtering decisions based all the data included in a
800+
/// span or event's [`Metadata`]. This means that it is possible for `enabled!`
801+
/// to return a _false positive_ (indicating that something would be enabled
802+
/// when it actually would not be) or a _false negative_ (indicating that
803+
/// something would be disabled when it would actually be enabled).
804+
///
805+
/// [Collectors]: crate::collect::Collect
806+
/// [`Metadata`]: crate::metadata::Metadata
807+
///
808+
/// This occurs when a subscriber is using a _more specific_ filter than the
809+
/// metadata provided to the `enabled!` macro. Some situations that can result
810+
/// in false positives or false negatives include:
811+
///
812+
/// - If a collector is using a filter which may enable a span or event based
813+
/// on field names, but `enabled!` is invoked without listing field names,
814+
/// `enabled!` may return a false negative if a specific field name would
815+
/// cause the collector to enable something that would otherwise be disabled.
816+
/// - If a collector is using a filter which enables or disables specific events by
817+
/// file path and line number, a particular event may be enabled/disabled
818+
/// even if an `enabled!` invocation with the same level, target, and fields
819+
/// indicated otherwise.
820+
/// - The collector can choose to enable _only_ spans or _only_ events, which `enabled`
821+
/// will not reflect.
822+
///
823+
/// `enabled!()` requires a [level](crate::Level) argument, an optional `target:`
824+
/// argument, and an optional set of field names. If the fields are not provided,
825+
/// they are considered to be unknown. `enabled!` attempts to match the
826+
/// syntax of `event!()` as closely as possible, which can be seen in the
827+
/// examples below.
828+
///
829+
/// # Examples
830+
///
831+
/// If the current collector is interested in recording `DEBUG`-level spans and
832+
/// events in the current file and module path, this will evaluate to true:
833+
/// ```rust
834+
/// use tracing::{enabled, Level};
835+
///
836+
/// if enabled!(Level::DEBUG) {
837+
/// // some expensive work...
838+
/// }
839+
/// ```
840+
///
841+
/// If the current collector is interested in recording spans and events
842+
/// in the current file and module path, with the target "my_crate", and at the
843+
/// level `DEBUG`, this will evaluate to true:
844+
/// ```rust
845+
/// # use tracing::{enabled, Level};
846+
/// if enabled!(target: "my_crate", Level::DEBUG) {
847+
/// // some expensive work...
848+
/// }
849+
/// ```
850+
///
851+
/// If the current collector is interested in recording spans and events
852+
/// in the current file and module path, with the target "my_crate", at
853+
/// the level `DEBUG`, and with a field named "hello", this will evaluate
854+
/// to true:
855+
///
856+
/// ```rust
857+
/// # use tracing::{enabled, Level};
858+
/// if enabled!(target: "my_crate", Level::DEBUG, hello) {
859+
/// // some expensive work...
860+
/// }
861+
/// ```
862+
///
863+
#[macro_export]
864+
macro_rules! enabled {
865+
(target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
866+
if $crate::level_enabled!($lvl) {
867+
use $crate::__macro_support::Callsite as _;
868+
static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
869+
name: concat!(
870+
"enabled ",
871+
file!(),
872+
":",
873+
line!()
874+
),
875+
kind: $crate::metadata::Kind::HINT,
876+
target: $target,
877+
level: $lvl,
878+
fields: $($fields)*
879+
};
880+
let interest = CALLSITE.interest();
881+
if !interest.is_never() && CALLSITE.is_enabled(interest) {
882+
let meta = CALLSITE.metadata();
883+
$crate::dispatch::get_default(|current| current.enabled(meta))
884+
} else {
885+
false
886+
}
887+
} else {
888+
false
889+
}
890+
});
891+
// Just target and level
892+
(target: $target:expr, $lvl:expr ) => (
893+
$crate::enabled!(target: $target, $lvl, { })
894+
);
895+
896+
// These two cases handle fields with no values
897+
(target: $target:expr, $lvl:expr, $($field:tt)*) => (
898+
$crate::enabled!(
899+
target: $target,
900+
$lvl,
901+
{ $($field)*}
902+
)
903+
);
904+
($lvl:expr, $($field:tt)*) => (
905+
$crate::enabled!(
906+
target: module_path!(),
907+
$lvl,
908+
{ $($field)*}
909+
)
910+
);
911+
912+
// Simplest `enabled!` case
913+
( $lvl:expr ) => (
914+
$crate::enabled!(target: module_path!(), $lvl, { })
915+
);
916+
}
917+
786918
/// Constructs an event at the trace level.
787919
///
788920
/// This functions similarly to the [`event!`] macro. See [the top-level

tracing/tests/enabled.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// liballoc is required because the test subscriber cannot be constructed
2+
// statically
3+
#![cfg(feature = "alloc")]
4+
5+
mod support;
6+
7+
use self::support::*;
8+
use tracing::Level;
9+
10+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
11+
#[test]
12+
fn level_and_target() {
13+
let (collector, handle) = collector::mock()
14+
.with_filter(|meta| {
15+
if meta.target() == "debug_module" {
16+
meta.level() <= &Level::DEBUG
17+
} else {
18+
meta.level() <= &Level::INFO
19+
}
20+
})
21+
.done()
22+
.run_with_handle();
23+
24+
tracing::collect::set_global_default(collector).unwrap();
25+
26+
assert!(tracing::enabled!(target: "debug_module", Level::DEBUG));
27+
assert!(tracing::enabled!(Level::ERROR));
28+
assert!(!tracing::enabled!(Level::DEBUG));
29+
handle.assert_finished();
30+
}

tracing/tests/macros.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![deny(warnings)]
22
use tracing::{
3-
callsite, debug, debug_span, error, error_span, event, info, info_span, span, trace,
3+
callsite, debug, debug_span, enabled, error, error_span, event, info, info_span, span, trace,
44
trace_span, warn, warn_span, Level,
55
};
66

@@ -334,6 +334,20 @@ fn event() {
334334
event!(Level::DEBUG, foo);
335335
}
336336

337+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
338+
#[test]
339+
fn enabled() {
340+
enabled!(Level::DEBUG, foo, bar.baz, quux,);
341+
enabled!(Level::DEBUG, message);
342+
enabled!(Level::INFO, foo, bar.baz, quux, message,);
343+
enabled!(Level::INFO, foo, bar., message,);
344+
enabled!(Level::DEBUG, foo);
345+
346+
enabled!(Level::DEBUG);
347+
enabled!(target: "rando", Level::DEBUG);
348+
enabled!(target: "rando", Level::DEBUG, field);
349+
}
350+
337351
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
338352
#[test]
339353
fn locals_with_message() {

0 commit comments

Comments
 (0)