Skip to content

Commit 2c3555b

Browse files
guswynnhawkwdavidbarsky
authored
tracing: add enabled! macro (#1883)
Backports #1821 to `v0.1.x`. ## 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 Co-authored-by: Eliza Weisman <[email protected]> Co-authored-by: David Barsky <[email protected]>
1 parent 7cf4854 commit 2c3555b

File tree

4 files changed

+184
-1
lines changed

4 files changed

+184
-1
lines changed

tracing-core/src/metadata.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ impl<'a> fmt::Debug for Metadata<'a> {
371371
enum KindInner {
372372
Event,
373373
Span,
374+
Hint,
374375
}
375376

376377
impl Kind {
@@ -380,6 +381,11 @@ impl Kind {
380381
/// `Span` callsite
381382
pub const SPAN: Kind = Kind(KindInner::Span);
382383

384+
/// `enabled!` callsite. [`Subscriber`][`crate::subscriber::Subscriber`]s can assume
385+
/// this `Kind` means they will never recieve a
386+
/// full event with this [`Metadata`].
387+
pub const HINT: Kind = Kind(KindInner::Hint);
388+
383389
/// Return true if the callsite kind is `Span`
384390
pub fn is_span(&self) -> bool {
385391
matches!(self, Kind(KindInner::Span))
@@ -389,6 +395,11 @@ impl Kind {
389395
pub fn is_event(&self) -> bool {
390396
matches!(self, Kind(KindInner::Event))
391397
}
398+
399+
/// Return true if the callsite kind is `Hint`
400+
pub fn is_hint(&self) -> bool {
401+
matches!(self, Kind(KindInner::Hint))
402+
}
392403
}
393404

394405
// ===== impl Level =====

tracing/src/macros.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,138 @@ macro_rules! event {
789789
);
790790
}
791791

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

tracing/tests/enabled.rs

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

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)