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, core: re-implement empty fields #548

Merged
merged 3 commits into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion tracing-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name = "tracing-core"
# - README.md
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.9"
version = "0.1.10"
authors = ["Tokio Contributors <team@tokio.rs>"]
license = "MIT"
readme = "README.md"
Expand Down
34 changes: 34 additions & 0 deletions tracing-core/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ pub struct Field {
fields: FieldSet,
}

/// An empty field.
///
/// This can be used to indicate that the value of a field is not currently
/// present but will be recorded later.
///
/// When a field's value is `Empty`. it will not be recorded.
#[derive(Debug, Eq, PartialEq)]
pub struct Empty;

/// Describes the fields present on a span.
pub struct FieldSet {
/// The names of each field on the described span.
Expand Down Expand Up @@ -456,6 +465,12 @@ impl<T: fmt::Debug> fmt::Debug for DebugValue<T> {
}
}

impl crate::sealed::Sealed for Empty {}
impl Value for Empty {
#[inline]
fn record(&self, _: &Field, _: &mut dyn Visit) {}
}

// ===== impl Field =====

impl Field {
Expand Down Expand Up @@ -865,6 +880,25 @@ mod test {
valueset.record(&mut MyVisitor);
}

#[test]
fn empty_fields_are_skipped() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), Some(&Empty as &dyn Value)),
(&fields.field("bar").unwrap(), Some(&57 as &dyn Value)),
(&fields.field("baz").unwrap(), Some(&Empty as &dyn Value)),
];

struct MyVisitor;
impl Visit for MyVisitor {
fn record_debug(&mut self, field: &Field, _: &dyn (crate::stdlib::fmt::Debug)) {
assert_eq!(field.name(), "bar")
}
}
let valueset = fields.value_set(values);
valueset.record(&mut MyVisitor);
}

#[test]
fn record_debug_fn() {
let fields = TEST_META_1.fields();
Expand Down
2 changes: 1 addition & 1 deletion tracing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ keywords = ["logging", "tracing", "metrics", "async"]
edition = "2018"

[dependencies]
tracing-core = { path = "../tracing-core", version = "0.1.9", default-features = false }
tracing-core = { path = "../tracing-core", version = "0.1.10", default-features = false }
log = { version = "0.4", optional = true }
tracing-attributes = "0.1.6"
cfg-if = "0.1.10"
Expand Down
18 changes: 18 additions & 0 deletions tracing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,23 @@
//! # }
//! ```
//!
//! Additionally, a span may declare fields with the special value [`Empty`],
//! which indicates that that the value for that field does not currently exist
//! but may be recorded later. For example:
//!
//! ```
//! use tracing::{trace_span, field};
//!
//! // Create a span with two fields: `greeting`, with the value "hello world", and
//! // `parting`, without a value.
//! let span = trace_span!("my_span", greeting = "hello world", parting = field::Empty);
//!
//! // ...
//!
//! // Now, record a value for parting as well.
//! span.record("parting", &"goodbye world!");
Comment on lines +343 to +344
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as I mentioned prior would be handy here too.

//! ```
//!
//! Note that a span may have up to 32 fields. The following will not compile:
//!
//! ```rust,compile_fail
Expand Down Expand Up @@ -379,6 +396,7 @@
//! [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
//! [`fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//! [fmt]: https://doc.rust-lang.org/std/fmt/#usage
//! [`Empty`]: field/struct.Empty.html
//!
//! ### Shorthand Macros
//!
Expand Down
40 changes: 38 additions & 2 deletions tracing/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,43 @@ impl Span {
self.field(field).is_some()
}

/// Visits that the field described by `field` has the value `value`.
/// Records that the field described by `field` has the value `value`.
///
/// This may be used with [`field::Empty`] to declare fields whose values
/// are not known when the span is created, and record them later:
/// ```
/// use tracing::{trace_span, field};
///
/// // Create a span with two fields: `greeting`, with the value "hello world", and
/// // `parting`, without a value.
/// let span = trace_span!("my_span", greeting = "hello world", parting = field::Empty);
///
/// // ...
///
/// // Now, record a value for parting as well.
/// span.record("parting", &"goodbye world!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since "parting" needs to referenced as a string, I'd add a comment noting that difference between macros and normal function parameters as it took me a few seconds to parse the example in https://deploy-preview-548--tracing-rs.netlify.com/tracing/span/struct.span?search=#method.record.

/// ```
/// However, it may also be used to record a _new_ value for a field whose
/// value was already recorded:
/// ```
/// use tracing::info_span;
/// # fn do_something() -> Result<(), ()> { Err(()) }
///
/// // Initially, let's assume that our attempt to do something is going okay...
/// let span = info_span!("doing_something", is_okay = true);
/// let _e = span.enter();
///
/// match do_something() {
/// Ok(something) => {
/// // ...
/// }
/// Err(_) => {
/// // Things are no longer okay!
/// span.record("is_okay", &false);
/// }
/// }
/// ```
/// [`field::Empty`]: ../field/struct.Empty.html
pub fn record<Q: ?Sized, V>(&self, field: &Q, value: &V) -> &Self
where
Q: field::AsField,
Expand All @@ -654,7 +690,7 @@ impl Span {
self
}

/// Visit all the fields in the span
/// Records all the fields in the provided `ValueSet`.
pub fn record_all(&self, values: &field::ValueSet<'_>) -> &Self {
let record = Record::new(values);
if let Some(ref inner) = self.inner {
Expand Down