Skip to content

Commit

Permalink
tracing: add enabled! macro (#1883)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
3 people authored Feb 2, 2022
1 parent 7cf4854 commit 2c3555b
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 1 deletion.
11 changes: 11 additions & 0 deletions tracing-core/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ impl<'a> fmt::Debug for Metadata<'a> {
enum KindInner {
Event,
Span,
Hint,
}

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

/// `enabled!` callsite. [`Subscriber`][`crate::subscriber::Subscriber`]s can assume
/// this `Kind` means they will never recieve a
/// full event with this [`Metadata`].
pub const HINT: Kind = Kind(KindInner::Hint);

/// Return true if the callsite kind is `Span`
pub fn is_span(&self) -> bool {
matches!(self, Kind(KindInner::Span))
Expand All @@ -389,6 +395,11 @@ impl Kind {
pub fn is_event(&self) -> bool {
matches!(self, Kind(KindInner::Event))
}

/// Return true if the callsite kind is `Hint`
pub fn is_hint(&self) -> bool {
matches!(self, Kind(KindInner::Hint))
}
}

// ===== impl Level =====
Expand Down
132 changes: 132 additions & 0 deletions tracing/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,138 @@ macro_rules! event {
);
}

/// Checks whether a span or event is [enabled] based on the provided [metadata].
///
/// [enabled]: crate::Subscriber::enabled
/// [metadata]: crate::Metadata
///
/// This macro is a specialized tool: it is intended to be used prior
/// to an expensive computation required *just* for that event, but
/// *cannot* be done as part of an argument to that event, such as
/// when multiple events are emitted (e.g., iterating over a collection
/// and emitting an event for each item).
///
/// # Usage
///
/// [Subscribers] can make filtering decisions based all the data included in a
/// span or event's [`Metadata`]. This means that it is possible for `enabled!`
/// to return a _false positive_ (indicating that something would be enabled
/// when it actually would not be) or a _false negative_ (indicating that
/// something would be disabled when it would actually be enabled).
///
/// [Subscribers]: crate::subscriber::Subscriber
/// [`Metadata`]: crate::metadata::Metadata
///
/// This occurs when a subscriber is using a _more specific_ filter than the
/// metadata provided to the `enabled!` macro. Some situations that can result
/// in false positives or false negatives include:
///
/// - If a subscriber is using a filter which may enable a span or event based
/// on field names, but `enabled!` is invoked without listing field names,
/// `enabled!` may return a false negative if a specific field name would
/// cause the subscriber to enable something that would otherwise be disabled.
/// - If a subscriber is using a filter which enables or disables specific events by
/// file path and line number, a particular event may be enabled/disabled
/// even if an `enabled!` invocation with the same level, target, and fields
/// indicated otherwise.
/// - The subscriber can choose to enable _only_ spans or _only_ events, which `enabled`
/// will not reflect.
///
/// `enabled!()` requires a [level](crate::Level) argument, an optional `target:`
/// argument, and an optional set of field names. If the fields are not provided,
/// they are considered to be unknown. `enabled!` attempts to match the
/// syntax of `event!()` as closely as possible, which can be seen in the
/// examples below.
///
/// # Examples
///
/// If the current subscriber is interested in recording `DEBUG`-level spans and
/// events in the current file and module path, this will evaluate to true:
/// ```rust
/// use tracing::{enabled, Level};
///
/// if enabled!(Level::DEBUG) {
/// // some expensive work...
/// }
/// ```
///
/// If the current subscriber is interested in recording spans and events
/// in the current file and module path, with the target "my_crate", and at the
/// level `DEBUG`, this will evaluate to true:
/// ```rust
/// # use tracing::{enabled, Level};
/// if enabled!(target: "my_crate", Level::DEBUG) {
/// // some expensive work...
/// }
/// ```
///
/// If the current subscriber is interested in recording spans and events
/// in the current file and module path, with the target "my_crate", at
/// the level `DEBUG`, and with a field named "hello", this will evaluate
/// to true:
///
/// ```rust
/// # use tracing::{enabled, Level};
/// if enabled!(target: "my_crate", Level::DEBUG, hello) {
/// // some expensive work...
/// }
/// ```
///
#[macro_export]
macro_rules! enabled {
(target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
if $crate::level_enabled!($lvl) {
use $crate::__macro_support::Callsite as _;
static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
name: concat!(
"enabled ",
file!(),
":",
line!()
),
kind: $crate::metadata::Kind::HINT,
target: $target,
level: $lvl,
fields: $($fields)*
};
let interest = CALLSITE.interest();
if !interest.is_never() && CALLSITE.is_enabled(interest) {
let meta = CALLSITE.metadata();
$crate::dispatcher::get_default(|current| current.enabled(meta))
} else {
false
}
} else {
false
}
});
// Just target and level
(target: $target:expr, $lvl:expr ) => (
$crate::enabled!(target: $target, $lvl, { })
);

// These two cases handle fields with no values
(target: $target:expr, $lvl:expr, $($field:tt)*) => (
$crate::enabled!(
target: $target,
$lvl,
{ $($field)*}
)
);
($lvl:expr, $($field:tt)*) => (
$crate::enabled!(
target: module_path!(),
$lvl,
{ $($field)*}
)
);

// Simplest `enabled!` case
( $lvl:expr ) => (
$crate::enabled!(target: module_path!(), $lvl, { })
);
}

/// Constructs an event at the trace level.
///
/// This functions similarly to the [`event!`] macro. See [the top-level
Expand Down
26 changes: 26 additions & 0 deletions tracing/tests/enabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
mod support;

use self::support::*;
use tracing::Level;

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn level_and_target() {
let (subscriber, handle) = subscriber::mock()
.with_filter(|meta| {
if meta.target() == "debug_module" {
meta.level() <= &Level::DEBUG
} else {
meta.level() <= &Level::INFO
}
})
.done()
.run_with_handle();

tracing::subscriber::set_global_default(subscriber).unwrap();

assert!(tracing::enabled!(target: "debug_module", Level::DEBUG));
assert!(tracing::enabled!(Level::ERROR));
assert!(!tracing::enabled!(Level::DEBUG));
handle.assert_finished();
}
16 changes: 15 additions & 1 deletion tracing/tests/macros.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![deny(warnings)]
use tracing::{
callsite, debug, debug_span, error, error_span, event, info, info_span, span, trace,
callsite, debug, debug_span, enabled, error, error_span, event, info, info_span, span, trace,
trace_span, warn, warn_span, Level,
};

Expand Down Expand Up @@ -334,6 +334,20 @@ fn event() {
event!(Level::DEBUG, foo);
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn enabled() {
enabled!(Level::DEBUG, foo, bar.baz, quux,);
enabled!(Level::DEBUG, message);
enabled!(Level::INFO, foo, bar.baz, quux, message,);
enabled!(Level::INFO, foo, bar., message,);
enabled!(Level::DEBUG, foo);

enabled!(Level::DEBUG);
enabled!(target: "rando", Level::DEBUG);
enabled!(target: "rando", Level::DEBUG, field);
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn locals_with_message() {
Expand Down

0 comments on commit 2c3555b

Please sign in to comment.