Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tracing: backport "implement enabled!" #1883

Merged
merged 4 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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. [`Collect`][`crate::collect::Collect`]s can assume
guswynn marked this conversation as resolved.
Show resolved Hide resolved
/// 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
30 changes: 30 additions & 0 deletions tracing/tests/enabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// liballoc is required because the test subscriber cannot be constructed
// statically
#![cfg(feature = "alloc")]
guswynn marked this conversation as resolved.
Show resolved Hide resolved

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 (collector, handle) = collector::mock()
guswynn marked this conversation as resolved.
Show resolved Hide resolved
.with_filter(|meta| {
if meta.target() == "debug_module" {
meta.level() <= &Level::DEBUG
} else {
meta.level() <= &Level::INFO
}
})
.done()
.run_with_handle();

tracing::collect::set_global_default(collector).unwrap();
guswynn marked this conversation as resolved.
Show resolved Hide resolved

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