diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index f4d89ead1f..ccd30acee6 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -29,7 +29,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 77a52ca806..c175cfe762 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'tokio-rs' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: taiki-e/create-gh-release-action@v1 with: prefix: tracing(-[a-z]+)? diff --git a/README.md b/README.md index 033efdc3f7..d98bcc624f 100644 --- a/README.md +++ b/README.md @@ -436,7 +436,7 @@ please let us know!) [Tracy]: https://github.com/wolfpld/tracy [`tracing-elastic-apm`]: https://crates.io/crates/tracing-elastic-apm [Elastic APM]: https://www.elastic.co/apm -[`tracing-etw`]: https://github.com/microsoft/tracing-etw +[`tracing-etw`]: https://github.com/microsoft/rust_win_etw/tree/main/win_etw_tracing [ETW]: https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing [`sentry-tracing`]: https://crates.io/crates/sentry-tracing [Sentry]: https://sentry.io/welcome/ diff --git a/examples/examples/sloggish/sloggish_subscriber.rs b/examples/examples/sloggish/sloggish_subscriber.rs index e551811557..fe42d3663a 100644 --- a/examples/examples/sloggish/sloggish_subscriber.rs +++ b/examples/examples/sloggish/sloggish_subscriber.rs @@ -38,7 +38,7 @@ pub struct CurrentSpanPerThread { impl CurrentSpanPerThread { pub fn new() -> Self { thread_local! { - static CURRENT: RefCell> = const { RefCell::new(vec![]) }; + static CURRENT: RefCell> = const { RefCell::new(Vec::new()) }; }; Self { current: &CURRENT } } @@ -239,6 +239,7 @@ impl Subscriber for SloggishSubscriber { self.print_indent(&mut stderr, indent).unwrap(); stack.push(span_id.clone()); if let Some(data) = data { + #[allow(clippy::map_identity)] // TODO remove in Rust 1.77 self.print_kvs(&mut stderr, data.kvs.iter().map(|(k, v)| (k, v)), "") .unwrap(); } diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index 8b4526164b..12525ec9e6 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -17,8 +17,8 @@ pub(crate) struct EventArgs { #[derive(Clone, Default, Debug)] pub(crate) struct InstrumentArgs { level: Option, - pub(crate) name: Option, - target: Option, + pub(crate) name: Option, + target: Option, pub(crate) parent: Option, pub(crate) follows_from: Option, pub(crate) skips: HashSet, @@ -87,6 +87,8 @@ impl Parse for InstrumentArgs { // XXX: apparently we support names as either named args with an // sign, _or_ as unnamed string literals. That's weird, but // changing it is apparently breaking. + // This also means that when using idents for name, it must be via + // a named arg, i.e. `#[instrument(name = SOME_IDENT)]`. if args.name.is_some() { return Err(input.error("expected only a single `name` argument")); } @@ -211,8 +213,32 @@ impl Parse for EventArgs { } } +#[derive(Debug, Clone)] +pub(super) enum LitStrOrIdent { + LitStr(LitStr), + Ident(Ident), +} + +impl ToTokens for LitStrOrIdent { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + LitStrOrIdent::LitStr(target) => target.to_tokens(tokens), + LitStrOrIdent::Ident(ident) => ident.to_tokens(tokens), + } + } +} + +impl Parse for LitStrOrIdent { + fn parse(input: ParseStream<'_>) -> syn::Result { + input + .parse::() + .map(LitStrOrIdent::LitStr) + .or_else(|_| input.parse::().map(LitStrOrIdent::Ident)) + } +} + struct StrArg { - value: LitStr, + value: LitStrOrIdent, _p: std::marker::PhantomData, } diff --git a/tracing-attributes/tests/fields.rs b/tracing-attributes/tests/fields.rs index 35b69967ec..3f9dce667e 100644 --- a/tracing-attributes/tests/fields.rs +++ b/tracing-attributes/tests/fields.rs @@ -34,6 +34,9 @@ fn fn_string(s: String) { let _ = s; } +#[instrument(fields(keywords.impl.type.fn = _arg), skip(_arg))] +fn fn_keyword_ident_in_field(_arg: &str) {} + #[derive(Debug)] struct HasField { my_field: &'static str, @@ -146,6 +149,16 @@ fn string_field() { }); } +#[test] +fn keyword_ident_in_field_name() { + let span = expect::span().with_fields( + expect::field("keywords.impl.type.fn") + .with_value(&"test") + .only(), + ); + run_test(span, || fn_keyword_ident_in_field("test")); +} + fn run_test T, T>(span: NewSpan, fun: F) { let (subscriber, handle) = subscriber::mock() .new_span(span) diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs index d01df0c313..402be8572a 100644 --- a/tracing-attributes/tests/instrument.rs +++ b/tracing-attributes/tests/instrument.rs @@ -252,3 +252,75 @@ fn impl_trait_return_type() { handle.assert_finished(); } + +#[test] +fn name_ident() { + const MY_NAME: &str = "my_name"; + #[instrument(name = MY_NAME)] + fn name() {} + + let span_name = expect::span().named(MY_NAME); + + let (subscriber, handle) = subscriber::mock() + .new_span(span_name.clone()) + .enter(span_name.clone()) + .exit(span_name.clone()) + .drop_span(span_name) + .only() + .run_with_handle(); + + with_default(subscriber, || { + name(); + }); + + handle.assert_finished(); +} + +#[test] +fn target_ident() { + const MY_TARGET: &str = "my_target"; + + #[instrument(target = MY_TARGET)] + fn target() {} + + let span_target = expect::span().named("target").with_target(MY_TARGET); + + let (subscriber, handle) = subscriber::mock() + .new_span(span_target.clone()) + .enter(span_target.clone()) + .exit(span_target.clone()) + .drop_span(span_target) + .only() + .run_with_handle(); + + with_default(subscriber, || { + target(); + }); + + handle.assert_finished(); +} + +#[test] +fn target_name_ident() { + const MY_NAME: &str = "my_name"; + const MY_TARGET: &str = "my_target"; + + #[instrument(target = MY_TARGET, name = MY_NAME)] + fn name_target() {} + + let span_name_target = expect::span().named(MY_NAME).with_target(MY_TARGET); + + let (subscriber, handle) = subscriber::mock() + .new_span(span_name_target.clone()) + .enter(span_name_target.clone()) + .exit(span_name_target.clone()) + .drop_span(span_name_target) + .only() + .run_with_handle(); + + with_default(subscriber, || { + name_target(); + }); + + handle.assert_finished(); +} diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs index deb56f7341..fc9f295844 100644 --- a/tracing-core/src/dispatcher.rs +++ b/tracing-core/src/dispatcher.rs @@ -183,10 +183,12 @@ enum Kind { #[cfg(feature = "std")] thread_local! { - static CURRENT_STATE: State = const { State { - default: RefCell::new(None), - can_enter: Cell::new(true), - } }; + static CURRENT_STATE: State = const { + State { + default: RefCell::new(None), + can_enter: Cell::new(true), + } + }; } static EXISTS: AtomicBool = AtomicBool::new(false); diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 2267407164..c09b863591 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -112,7 +112,7 @@ use crate::callsite; use crate::stdlib::{ borrow::Borrow, - fmt, + fmt::{self, Write}, hash::{Hash, Hasher}, num, ops::Range, @@ -308,6 +308,11 @@ pub trait Visit { self.record_debug(field, &value) } + /// Visit a byte slice. + fn record_bytes(&mut self, field: &Field, value: &[u8]) { + self.record_debug(field, &HexBytes(value)) + } + /// Records a type implementing `Error`. /// ///
@@ -380,6 +385,26 @@ where t.as_value() } +struct HexBytes<'a>(&'a [u8]); + +impl<'a> fmt::Debug for HexBytes<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_char('[')?; + + let mut bytes = self.0.iter(); + + if let Some(byte) = bytes.next() { + f.write_fmt(format_args!("{byte:02x}"))?; + } + + for byte in bytes { + f.write_fmt(format_args!(" {byte:02x}"))?; + } + + f.write_char(']') + } +} + // ===== impl Visit ===== impl<'a, 'b> Visit for fmt::DebugStruct<'a, 'b> { @@ -540,6 +565,14 @@ impl Value for str { } } +impl crate::sealed::Sealed for [u8] {} + +impl Value for [u8] { + fn record(&self, key: &Field, visitor: &mut dyn Visit) { + visitor.record_bytes(key, self) + } +} + #[cfg(feature = "std")] impl crate::sealed::Sealed for dyn std::error::Error + 'static {} @@ -771,6 +804,11 @@ impl Field { pub fn name(&self) -> &'static str { self.fields.names[self.i] } + + /// Returns the index of this field in its [`FieldSet`]. + pub fn index(&self) -> usize { + self.i + } } impl fmt::Display for Field { @@ -1141,6 +1179,17 @@ mod test { assert!(valueset.is_empty()); } + #[test] + fn index_of_field_in_fieldset_is_correct() { + let fields = TEST_META_1.fields(); + let foo = fields.field("foo").unwrap(); + assert_eq!(foo.index(), 0); + let bar = fields.field("bar").unwrap(); + assert_eq!(bar.index(), 1); + let baz = fields.field("baz").unwrap(); + assert_eq!(baz.index(), 2); + } + #[test] fn empty_value_set_is_empty() { let fields = TEST_META_1.fields(); @@ -1249,4 +1298,23 @@ mod test { }); assert_eq!(result, format!("{}", err)); } + + #[test] + fn record_bytes() { + let fields = TEST_META_1.fields(); + let first = &b"abc"[..]; + let second: &[u8] = &[192, 255, 238]; + let values = &[ + (&fields.field("foo").unwrap(), Some(&first as &dyn Value)), + (&fields.field("bar").unwrap(), Some(&" " as &dyn Value)), + (&fields.field("baz").unwrap(), Some(&second as &dyn Value)), + ]; + let valueset = fields.value_set(values); + let mut result = String::new(); + valueset.record(&mut |_: &Field, value: &dyn fmt::Debug| { + use core::fmt::Write; + write!(&mut result, "{:?}", value).unwrap(); + }); + assert_eq!(result, format!("{}", r#"[61 62 63]" "[c0 ff ee]"#)); + } } diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index bcb13533da..4286d88c33 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -147,6 +147,14 @@ #[cfg(not(feature = "std"))] extern crate alloc; +#[doc(hidden)] +pub mod __macro_support { + // Re-export the `core` functions that are used in macros. This allows + // a crate to be named `core` and avoid name clashes. + // See here: https://github.com/tokio-rs/tracing/issues/2761 + pub use core::{file, line, module_path, option::Option}; +} + /// Statically constructs an [`Identifier`] for the provided [`Callsite`]. /// /// This may be used in contexts such as static initializers. @@ -244,9 +252,9 @@ macro_rules! metadata { $name, $target, $level, - ::core::option::Option::Some(file!()), - ::core::option::Option::Some(line!()), - ::core::option::Option::Some(module_path!()), + $crate::__macro_support::Option::Some($crate::__macro_support::file!()), + $crate::__macro_support::Option::Some($crate::__macro_support::line!()), + $crate::__macro_support::Option::Some($crate::__macro_support::module_path!()), $crate::field::FieldSet::new($fields, $crate::identify_callsite!($callsite)), $kind, ) diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index 7d8448b641..5cec421899 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -377,7 +377,7 @@ impl Kind { pub const SPAN: Kind = Kind(Self::SPAN_BIT); /// `enabled!` callsite. [`Subscriber`][`crate::subscriber::Subscriber`]s can assume - /// this `Kind` means they will never recieve a + /// this `Kind` means they will never receive a /// full event with this [`Metadata`]. pub const HINT: Kind = Kind(Self::HINT_BIT); diff --git a/tracing-error/CHANGELOG.md b/tracing-error/CHANGELOG.md index 7cd565dcfe..bd966591dc 100644 --- a/tracing-error/CHANGELOG.md +++ b/tracing-error/CHANGELOG.md @@ -35,7 +35,7 @@ Thanks to @CAD97 for contributing to this release! - **TracedError**: `TracedError`, an error type wrapper that annotates an error with the current span. - **SpanTrace**:`SpanTrace::status` method and `SpanTraceStatus` type for - determing whether a `SpanTrace` was successfully captured (#614) + determining whether a `SpanTrace` was successfully captured (#614) ### Changed diff --git a/tracing-error/src/error.rs b/tracing-error/src/error.rs index f28d0c39fd..74c50fb445 100644 --- a/tracing-error/src/error.rs +++ b/tracing-error/src/error.rs @@ -73,8 +73,8 @@ where // erased `ErrorImpl` back to its original type, which is needed in order to forward our // error/display/debug impls to the internal error type from the type erased error type. // - // The repr(C) is necessary to ensure that the struct is layed out in the order we - // specified it so that we can safely access the vtable and spantrace fields thru a type + // The repr(C) is necessary to ensure that the struct is laid out in the order we + // specified it, so that we can safely access the vtable and spantrace fields through a type // erased pointer to the original object. let vtable = &ErrorVTable { object_ref: object_ref::, diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index d7229af801..3f0e561284 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -60,7 +60,7 @@ mod socket; /// names by translating `.`s into `_`s, stripping leading `_`s and non-ascii-alphanumeric /// characters other than `_`, and upcasing. /// -/// Levels are mapped losslessly to journald `PRIORITY` values as follows: +/// By default, levels are mapped losslessly to journald `PRIORITY` values as follows: /// /// - `ERROR` => Error (3) /// - `WARN` => Warning (4) @@ -68,6 +68,8 @@ mod socket; /// - `DEBUG` => Informational (6) /// - `TRACE` => Debug (7) /// +/// These mappings can be changed with [`Subscriber::with_priority_mappings`]. +/// /// The standard journald `CODE_LINE` and `CODE_FILE` fields are automatically emitted. A `TARGET` /// field is emitted containing the event's target. /// @@ -84,6 +86,7 @@ pub struct Layer { field_prefix: Option, syslog_identifier: String, additional_fields: Vec, + priority_mappings: PriorityMappings, } #[cfg(unix)] @@ -109,6 +112,7 @@ impl Layer { // If we fail to get the name of the current executable fall back to an empty string. .unwrap_or_default(), additional_fields: Vec::new(), + priority_mappings: PriorityMappings::new(), }; // Check that we can talk to journald, by sending empty payload which journald discards. // However if the socket didn't exist or if none listened we'd get an error here. @@ -129,6 +133,41 @@ impl Layer { self } + /// Sets how [`tracing_core::Level`]s are mapped to [journald priorities](Priority). + /// + /// # Examples + /// + /// ```rust + /// use tracing_journald::{Priority, PriorityMappings}; + /// use tracing_subscriber::prelude::*; + /// use tracing::error; + /// + /// let registry = tracing_subscriber::registry(); + /// match tracing_journald::layer() { + /// Ok(layer) => { + /// registry.with( + /// layer + /// // We can tweak the mappings between the trace level and + /// // the journal priorities. + /// .with_priority_mappings(PriorityMappings { + /// info: Priority::Informational, + /// ..PriorityMappings::new() + /// }), + /// ); + /// } + /// // journald is typically available on Linux systems, but nowhere else. Portable software + /// // should handle its absence gracefully. + /// Err(e) => { + /// registry.init(); + /// error!("couldn't connect to journald: {}", e); + /// } + /// } + /// ``` + pub fn with_priority_mappings(mut self, mappings: PriorityMappings) -> Self { + self.priority_mappings = mappings; + self + } + /// Sets the syslog identifier for this logger. /// /// The syslog identifier comes from the classic syslog interface (`openlog()` @@ -232,6 +271,20 @@ impl Layer { memfd::seal_fully(mem.as_raw_fd())?; socket::send_one_fd_to(&self.socket, mem.as_raw_fd(), JOURNALD_PATH) } + + fn put_priority(&self, buf: &mut Vec, meta: &Metadata) { + put_field_wellformed( + buf, + "PRIORITY", + &[match *meta.level() { + Level::ERROR => self.priority_mappings.error as u8, + Level::WARN => self.priority_mappings.warn as u8, + Level::INFO => self.priority_mappings.info as u8, + Level::DEBUG => self.priority_mappings.debug as u8, + Level::TRACE => self.priority_mappings.trace as u8, + }], + ); + } } /// Construct a journald layer @@ -286,7 +339,7 @@ where } // Record event fields - put_priority(&mut buf, event.metadata()); + self.put_priority(&mut buf, event.metadata()); put_metadata(&mut buf, event.metadata(), None); put_field_length_encoded(&mut buf, "SYSLOG_IDENTIFIER", |buf| { write!(buf, "{}", self.syslog_identifier).unwrap() @@ -374,18 +427,114 @@ impl Visit for EventVisitor<'_> { } } -fn put_priority(buf: &mut Vec, meta: &Metadata) { - put_field_wellformed( - buf, - "PRIORITY", - match *meta.level() { - Level::ERROR => b"3", - Level::WARN => b"4", - Level::INFO => b"5", - Level::DEBUG => b"6", - Level::TRACE => b"7", - }, - ); +/// A priority (called "severity code" by syslog) is used to mark the +/// importance of a message. +/// +/// Descriptions and examples are taken from the [Arch Linux wiki]. +/// Priorities are also documented in the +/// [section 6.2.1 of the Syslog protocol RFC][syslog]. +/// +/// [Arch Linux wiki]: https://wiki.archlinux.org/title/Systemd/Journal#Priority_level +/// [syslog]: https://www.rfc-editor.org/rfc/rfc5424#section-6.2.1 +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[repr(u8)] +pub enum Priority { + /// System is unusable. + /// + /// Examples: + /// + /// - severe Kernel BUG + /// - systemd dumped core + /// + /// This level should not be used by applications. + Emergency = b'0', + /// Should be corrected immediately. + /// + /// Examples: + /// + /// - Vital subsystem goes out of work, data loss: + /// - `kernel: BUG: unable to handle kernel paging request at ffffc90403238ffc` + Alert = b'1', + /// Critical conditions + /// + /// Examples: + /// + /// - Crashe, coredumps + /// - `systemd-coredump[25319]: Process 25310 (plugin-container) of user 1000 dumped core` + Critical = b'2', + /// Error conditions + /// + /// Examples: + /// + /// - Not severe error reported + /// - `kernel: usb 1-3: 3:1: cannot get freq at ep 0x84, systemd[1]: Failed unmounting /var` + /// - `libvirtd[1720]: internal error: Failed to initialize a valid firewall backend` + Error = b'3', + /// May indicate that an error will occur if action is not taken. + /// + /// Examples: + /// + /// - a non-root file system has only 1GB free + /// - `org.freedesktop. Notifications[1860]: (process:5999): Gtk-WARNING **: Locale not supported by C library. Using the fallback 'C' locale` + Warning = b'4', + /// Events that are unusual, but not error conditions. + /// + /// Examples: + /// + /// - `systemd[1]: var.mount: Directory /var to mount over is not empty, mounting anyway` + /// - `gcr-prompter[4997]: Gtk: GtkDialog mapped without a transient parent. This is discouraged` + Notice = b'5', + /// Normal operational messages that require no action. + /// + /// Example: `lvm[585]: 7 logical volume(s) in volume group "archvg" now active` + Informational = b'6', + /// Information useful to developers for debugging the + /// application. + /// + /// Example: `kdeinit5[1900]: powerdevil: Scheduling inhibition from ":1.14" "firefox" with cookie 13 and reason "screen"` + Debug = b'7', +} + +/// Mappings from tracing [`Level`]s to journald [priorities]. +/// +/// [priorities]: Priority +#[derive(Debug, Clone)] +pub struct PriorityMappings { + /// Priority mapped to the `ERROR` level + pub error: Priority, + /// Priority mapped to the `WARN` level + pub warn: Priority, + /// Priority mapped to the `INFO` level + pub info: Priority, + /// Priority mapped to the `DEBUG` level + pub debug: Priority, + /// Priority mapped to the `TRACE` level + pub trace: Priority, +} + +impl PriorityMappings { + /// Returns the default priority mappings: + /// + /// - [`tracing::Level::ERROR`]: [`Priority::Error`] (3) + /// - [`tracing::Level::WARN`]: [`Priority::Warning`] (4) + /// - [`tracing::Level::INFO`]: [`Priority::Notice`] (5) + /// - [`tracing::Level::DEBUG`]: [`Priority::Informational`] (6) + /// - [`tracing::Level::TRACE`]: [`Priority::Debug`] (7) + pub fn new() -> PriorityMappings { + Self { + error: Priority::Error, + warn: Priority::Warning, + info: Priority::Notice, + debug: Priority::Informational, + trace: Priority::Debug, + } + } +} + +impl Default for PriorityMappings { + fn default() -> Self { + Self::new() + } } fn put_metadata(buf: &mut Vec, meta: &Metadata, prefix: Option<&str>) { diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs index c2f3010879..65379c1fd2 100644 --- a/tracing-journald/tests/journal.rs +++ b/tracing-journald/tests/journal.rs @@ -6,8 +6,8 @@ use std::time::Duration; use serde::Deserialize; -use tracing::{debug, error, info, info_span, warn}; -use tracing_journald::Layer; +use tracing::{debug, error, info, info_span, trace, warn}; +use tracing_journald::{Layer, Priority, PriorityMappings}; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::Registry; @@ -17,7 +17,16 @@ fn journalctl_version() -> std::io::Result { } fn with_journald(f: impl FnOnce()) { - with_journald_layer(Layer::new().unwrap().with_field_prefix(None), f) + with_journald_layer( + Layer::new() + .unwrap() + .with_field_prefix(None) + .with_priority_mappings(PriorityMappings { + trace: Priority::Informational, + ..PriorityMappings::new() + }), + f, + ) } fn with_journald_layer(layer: Layer, f: impl FnOnce()) { @@ -168,6 +177,41 @@ fn simple_message() { }); } +#[test] +fn custom_priorities() { + fn check_message(level: &str, priority: &str) { + let entry = retry_read_one_line_from_journal(&format!("custom_priority.{}", level)); + assert_eq!(entry["MESSAGE"], format!("hello {}", level).as_str()); + assert_eq!(entry["PRIORITY"], priority); + } + + let priorities = PriorityMappings { + error: Priority::Critical, + warn: Priority::Error, + info: Priority::Warning, + debug: Priority::Notice, + trace: Priority::Informational, + }; + let layer = Layer::new() + .unwrap() + .with_field_prefix(None) + .with_priority_mappings(priorities); + let test = || { + trace!(test.name = "custom_priority.trace", "hello trace"); + check_message("trace", "6"); + debug!(test.name = "custom_priority.debug", "hello debug"); + check_message("debug", "5"); + info!(test.name = "custom_priority.info", "hello info"); + check_message("info", "4"); + warn!(test.name = "custom_priority.warn", "hello warn"); + check_message("warn", "3"); + error!(test.name = "custom_priority.error", "hello error"); + check_message("error", "2"); + }; + + with_journald_layer(layer, test); +} + #[test] fn multiline_message() { with_journald(|| { diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs index 801280e0ff..eb29e8180b 100644 --- a/tracing-serde/src/lib.rs +++ b/tracing-serde/src/lib.rs @@ -75,6 +75,12 @@ //! next_id: AtomicUsize, // you need to assign span IDs, so you need a counter //! } //! +//! impl JsonSubscriber { +//! fn new() -> Self { +//! Self { next_id: 1.into() } +//! } +//! } +//! //! impl Subscriber for JsonSubscriber { //! //! fn new_span(&self, attrs: &Attributes<'_>) -> Id { @@ -97,7 +103,7 @@ //! } //! //! // ... -//! # fn enabled(&self, _: &Metadata<'_>) -> bool { false } +//! # fn enabled(&self, _: &Metadata<'_>) -> bool { true } //! # fn enter(&self, _: &Id) {} //! # fn exit(&self, _: &Id) {} //! # fn record(&self, _: &Id, _: &Record<'_>) {} @@ -199,6 +205,18 @@ use tracing_core::{ pub mod fields; +#[derive(Debug)] +pub struct SerializeField<'a>(&'a Field); + +impl<'a> Serialize for SerializeField<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.0.name()) + } +} + #[derive(Debug)] pub struct SerializeFieldSet<'a>(&'a FieldSet); @@ -209,7 +227,7 @@ impl<'a> Serialize for SerializeFieldSet<'a> { { let mut seq = serializer.serialize_seq(Some(self.0.len()))?; for element in self.0 { - seq.serialize_element(element.name())?; + seq.serialize_element(&SerializeField(&element))?; } seq.end() } @@ -552,6 +570,14 @@ impl<'a> AsSerde<'a> for Level { } } +impl<'a> AsSerde<'a> for Field { + type Serializable = SerializeField<'a>; + + fn as_serde(&'a self) -> Self::Serializable { + SerializeField(self) + } +} + impl<'a> AsSerde<'a> for FieldSet { type Serializable = SerializeFieldSet<'a>; @@ -572,6 +598,8 @@ impl<'a> self::sealed::Sealed for Record<'a> {} impl<'a> self::sealed::Sealed for Metadata<'a> {} +impl self::sealed::Sealed for Field {} + impl self::sealed::Sealed for FieldSet {} mod sealed { diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md index a8894b9ea2..7f0c551e89 100644 --- a/tracing-subscriber/CHANGELOG.md +++ b/tracing-subscriber/CHANGELOG.md @@ -972,7 +972,7 @@ Thanks to @bdonlan and @jeromegn for contributing to this release! ### Changed - **filter**: `LevelFilter` is now a re-export of the - `tracing_core::LevelFilter` type, it can now be used interchangably with the + `tracing_core::LevelFilter` type, it can now be used interchangeably with the versions in `tracing` and `tracing-core` (#853) - **filter**: Significant performance improvements when comparing `LevelFilter`s and `Level`s (#853) @@ -1175,7 +1175,7 @@ tuning in this release! - **fmt**: Fixed empty `{}` printed after spans with no fields (f079f2d) - **fmt**: Fixed inconsistent formatting when ANSI colors are disabled (506a482) -- **fmt**: Fixed mis-aligned levels when ANSI colors are disabled (eba1adb) +- **fmt**: Fixed misaligned levels when ANSI colors are disabled (eba1adb) - Fixed warnings on nightly Rust compilers (#558) # 0.2.0-alpha.5 (January 31, 2020) @@ -1262,9 +1262,9 @@ tuning in this release! changes in subsequent alpha. (#420, #425) - **BREAKING**: Removed `Filter`. Use `EnvFilter` instead (#434) -### Contributers +### Contributors -Thanks to all the contributers to this release! +Thanks to all the contributors to this release! - @pimeys for #377 and #415 @@ -1290,9 +1290,9 @@ Thanks to all the contributers to this release! order to initialize the global logger. Only `tracing-log` needs to be specified now (#400). -### Contributers +### Contributors -Thanks to all the contributers to this release! +Thanks to all the contributors to this release! - @emschwartz for #385, #387, #400 and #401 - @bIgBV for #388 diff --git a/tracing-subscriber/src/filter/env/mod.rs b/tracing-subscriber/src/filter/env/mod.rs index 81a9ae2bde..813e32b447 100644 --- a/tracing-subscriber/src/filter/env/mod.rs +++ b/tracing-subscriber/src/filter/env/mod.rs @@ -479,7 +479,7 @@ impl EnvFilter { let level = metadata.level(); // is it possible for a dynamic filter directive to enable this event? - // if not, we can avoid the thread loca'l access + iterating over the + // if not, we can avoid the thread local access + iterating over the // spans in the current scope. if self.has_dynamics && self.dynamics.max_level >= *level { if metadata.is_span() { diff --git a/tracing-subscriber/src/filter/filter_fn.rs b/tracing-subscriber/src/filter/filter_fn.rs index 332bf860a6..b57c30b294 100644 --- a/tracing-subscriber/src/filter/filter_fn.rs +++ b/tracing-subscriber/src/filter/filter_fn.rs @@ -295,7 +295,7 @@ where metadata: &'static Metadata<'static>, ) -> Interest { // Because `self.enabled` takes a `Metadata` only (and no `Context` - // parameter), we can reasonably assume its results are cachable, and + // parameter), we can reasonably assume its results are cacheable, and // just return `Interest::always`/`Interest::never`. if (self.enabled)(metadata) { debug_assert!( diff --git a/tracing-subscriber/src/filter/layer_filters/mod.rs b/tracing-subscriber/src/filter/layer_filters/mod.rs index 040eb705f7..f349d4ce6a 100644 --- a/tracing-subscriber/src/filter/layer_filters/mod.rs +++ b/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -99,12 +99,18 @@ pub struct FilterId(u64); /// /// [`Registry`]: crate::Registry /// [`Filter`]: crate::layer::Filter -#[derive(Default, Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] pub(crate) struct FilterMap { bits: u64, } -/// The current state of `enabled` calls to per-layer filters on this +impl FilterMap { + pub(crate) const fn new() -> Self { + Self { bits: 0 } + } +} + +/// The current state of `enabled` calls to per-subscriber filters on this /// thread. /// /// When `Filtered::enabled` is called, the filter will set the bit @@ -145,7 +151,7 @@ pub(crate) struct FilterState { /// Extra counters added to `FilterState` used only to make debug assertions. #[cfg(debug_assertions)] -#[derive(Debug, Default)] +#[derive(Debug)] struct DebugCounters { /// How many per-layer filters have participated in the current `enabled` /// call? @@ -156,8 +162,18 @@ struct DebugCounters { in_interest_pass: Cell, } +#[cfg(debug_assertions)] +impl DebugCounters { + const fn new() -> Self { + Self { + in_filter_pass: Cell::new(0), + in_interest_pass: Cell::new(0), + } + } +} + thread_local! { - pub(crate) static FILTERING: FilterState = FilterState::new(); + pub(crate) static FILTERING: FilterState = const { FilterState::new() }; } /// Extension trait adding [combinators] for combining [`Filter`]. @@ -721,7 +737,7 @@ where // // it would be cool if there was some wild rust reflection way of checking // if a trait impl has the default impl of a trait method or not, but that's - // almsot certainly impossible...right? + // almost certainly impossible...right? fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { let interest = self.filter.callsite_enabled(metadata); @@ -1080,13 +1096,13 @@ impl fmt::Binary for FilterMap { // === impl FilterState === impl FilterState { - fn new() -> Self { + const fn new() -> Self { Self { - enabled: Cell::new(FilterMap::default()), + enabled: Cell::new(FilterMap::new()), interest: RefCell::new(None), #[cfg(debug_assertions)] - counters: DebugCounters::default(), + counters: DebugCounters::new(), } } @@ -1095,7 +1111,7 @@ impl FilterState { { let in_current_pass = self.counters.in_filter_pass.get(); if in_current_pass == 0 { - debug_assert_eq!(self.enabled.get(), FilterMap::default()); + debug_assert_eq!(self.enabled.get(), FilterMap::new()); } self.counters.in_filter_pass.set(in_current_pass + 1); debug_assert_eq!( @@ -1140,7 +1156,7 @@ impl FilterState { #[cfg(debug_assertions)] { if this.counters.in_filter_pass.get() == 0 { - debug_assert_eq!(this.enabled.get(), FilterMap::default()); + debug_assert_eq!(this.enabled.get(), FilterMap::new()); } // Nothing enabled this event, we won't tick back down the @@ -1177,7 +1193,7 @@ impl FilterState { { let in_current_pass = self.counters.in_filter_pass.get(); if in_current_pass <= 1 { - debug_assert_eq!(self.enabled.get(), FilterMap::default()); + debug_assert_eq!(self.enabled.get(), FilterMap::new()); } self.counters .in_filter_pass @@ -1207,7 +1223,7 @@ impl FilterState { // a panic and the thread-local has been torn down, that's fine, just // ignore it ratehr than panicking. let _ = FILTERING.try_with(|filtering| { - filtering.enabled.set(FilterMap::default()); + filtering.enabled.set(FilterMap::new()); #[cfg(debug_assertions)] filtering.counters.in_filter_pass.set(0); @@ -1232,10 +1248,8 @@ impl FilterState { pub(crate) fn filter_map(&self) -> FilterMap { let map = self.enabled.get(); #[cfg(debug_assertions)] - { - if self.counters.in_filter_pass.get() == 0 { - debug_assert_eq!(map, FilterMap::default()); - } + if self.counters.in_filter_pass.get() == 0 { + debug_assert_eq!(map, FilterMap::new()); } map diff --git a/tracing-subscriber/src/filter/mod.rs b/tracing-subscriber/src/filter/mod.rs index 000a271951..045216721c 100644 --- a/tracing-subscriber/src/filter/mod.rs +++ b/tracing-subscriber/src/filter/mod.rs @@ -39,7 +39,7 @@ feature! { pub use self::directive::ParseError; } -/// Stub implementations of the per-layer-fitler detection functions for when the +/// Stub implementations of the per-layer-filter detection functions for when the /// `registry` feature is disabled. #[cfg(not(all(feature = "registry", feature = "std")))] mod has_plf_stubs { diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 54c84e422c..399b0eba27 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -241,6 +241,27 @@ impl Layer { self.is_ansi = ansi; } + /// Modifies how synthesized events are emitted at points in the [span + /// lifecycle][lifecycle]. + /// + /// See [`Self::with_span_events`] for documentation on the [`FmtSpan`] + /// + /// This method is primarily expected to be used with the + /// [`reload::Handle::modify`](crate::reload::Handle::modify) method + /// + /// Note that using this method modifies the span configuration instantly and does not take into + /// account any current spans. If the previous configuration was set to capture + /// `FmtSpan::ALL`, for example, using this method to change to `FmtSpan::NONE` will cause an + /// exit event for currently entered events not to be formatted + /// + /// [lifecycle]: mod@tracing::span#the-span-lifecycle + pub fn set_span_events(&mut self, kind: FmtSpan) { + self.fmt_span = format::FmtSpanConfig { + kind, + fmt_timing: self.fmt_span.fmt_timing, + } + } + /// Configures the layer to support [`libtest`'s output capturing][capturing] when used in /// unit tests. /// @@ -1587,4 +1608,52 @@ mod test { // dropping `_saved_no_color` will restore the previous value of // `NO_COLOR`. } + + // Validates that span event configuration can be modified with a reload handle + #[test] + fn modify_span_events() { + let make_writer = MockMakeWriter::default(); + + let inner_layer = fmt::Layer::default() + .with_writer(make_writer.clone()) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime) + .with_span_events(FmtSpan::ACTIVE); + + let (reloadable_layer, reload_handle) = + crate::reload::Layer::new(inner_layer); + let reload = reloadable_layer.with_subscriber(Registry::default()); + + with_default(reload, || { + { + let span1 = tracing::info_span!("span1", x = 42); + let _e = span1.enter(); + } + + let _ = reload_handle.modify(|s| s.set_span_events(FmtSpan::NONE)); + + // this span should not be logged at all! + { + let span2 = tracing::info_span!("span2", x = 100); + let _e = span2.enter(); + } + + { + let span3 = tracing::info_span!("span3", x = 42); + let _e = span3.enter(); + + // The span config was modified after span3 was already entered. + // We should only see an exit + let _ = reload_handle.modify(|s| s.set_span_events(FmtSpan::ACTIVE)); + } + }); + let actual = sanitize_timings(make_writer.get_string()); + assert_eq!( + "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: enter\n\ + fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: exit\n\ + fake time span3{x=42}: tracing_subscriber::fmt::fmt_layer::test: exit\n", + actual.as_str() + ); + } } diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index 7799abb6e0..c8cb84a472 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -525,6 +525,11 @@ impl<'a> field::Visit for JsonVisitor<'a> { .insert(field.name(), serde_json::Value::from(value)); } + fn record_bytes(&mut self, field: &Field, value: &[u8]) { + self.values + .insert(field.name(), serde_json::Value::from(value)); + } + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { match field.name() { // Skip fields that are actually log metadata that have already been handled @@ -564,13 +569,19 @@ mod test { #[test] fn json() { let expected = - "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; + "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); test_json(expected, subscriber, || { - let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); + let span = tracing::span!( + tracing::Level::INFO, + "json_span", + answer = 42, + number = 3, + slice = &b"abc"[..] + ); let _guard = span.enter(); tracing::info!("some json test"); }); diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index 4e36ca0069..6fbae11296 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -1829,7 +1829,7 @@ pub(super) mod test { "^fake time tracing_subscriber::fmt::format::test: {}:[0-9]+: hello\n$", current_path() // if we're on Windows, the path might contain backslashes, which - // have to be escpaed before compiling the regex. + // have to be escaped before compiling the regex. .replace('\\', "\\\\") )) .unwrap(); diff --git a/tracing-subscriber/src/fmt/time/datetime.rs b/tracing-subscriber/src/fmt/time/datetime.rs index 2a1d3cfbb5..d08ed55873 100644 --- a/tracing-subscriber/src/fmt/time/datetime.rs +++ b/tracing-subscriber/src/fmt/time/datetime.rs @@ -387,7 +387,7 @@ mod tests { case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0); case("1901-12-13T20:45:51.000000Z", i32::MIN as i64 - 1, 0); - // Skipping these tests on windows as std::time::SysteTime range is low + // Skipping these tests on windows as std::time::SystemTime range is low // on Windows compared with that of Unix which can cause the following // high date value tests to panic #[cfg(not(target_os = "windows"))] diff --git a/tracing-subscriber/src/layer/context.rs b/tracing-subscriber/src/layer/context.rs index 46254994f1..5ab6f84821 100644 --- a/tracing-subscriber/src/layer/context.rs +++ b/tracing-subscriber/src/layer/context.rs @@ -316,11 +316,10 @@ where /// Returns an iterator over the [stored data] for all the spans in the /// current context, starting with the specified span and ending with the - /// root of the trace tree and ending with the current span. + /// root of the trace tree. /// ///
-    /// Note: Compared to scope this
-    /// returns the spans in reverse order (from leaf to root). Use
+    /// Note: This returns the spans in reverse order (from leaf to root). Use
     /// Scope::from_root
     /// in case root-to-leaf ordering is desired.
     /// 
diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index 8543e2b08c..7b22cd3aa6 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -428,7 +428,7 @@ //! callsite). See [`Subscriber::register_callsite`] and //! [`tracing_core::callsite`] for a summary of how this behaves. //! - [`enabled`], once per emitted event (roughly: once per time that `event!` -//! or `span!` is *executed*), and only if `register_callsite` regesters an +//! or `span!` is *executed*), and only if `register_callsite` registers an //! [`Interest::sometimes`]. This is the main customization point to globally //! filter events based on their [`Metadata`]. If an event can be disabled //! based only on [`Metadata`], it should be, as this allows the construction @@ -764,7 +764,7 @@ where /// [`Subscriber`] has been set as the default, both the `Layer` and /// [`Subscriber`] are passed to this method _mutably_. This gives the /// `Layer` the opportunity to set any of its own fields with values - /// recieved by method calls on the [`Subscriber`]. + /// received by method calls on the [`Subscriber`]. /// /// For example, [`Filtered`] layers implement `on_layer` to call the /// [`Subscriber`]'s [`register_filter`] method, and store the returned @@ -1292,7 +1292,7 @@ feature! { ///
         /// Note: If a Filter will perform
         /// dynamic filtering that depends on the current context in which
-        /// a span or event was observered (e.g. only enabling an event when it
+        /// a span or event was observed (e.g. only enabling an event when it
         /// occurs within a particular span), it must return
         /// Interest::sometimes() from this method. If it returns
         /// Interest::always() or Interest::never(), the
@@ -1310,7 +1310,7 @@ feature! {
         /// other hand, when a `Filter` returns [`Interest::always()`][always] or
         /// [`Interest::never()`][never] for a callsite, _other_ [`Layer`]s may have
         /// differing interests in that callsite. If this is the case, the callsite
-        /// will recieve [`Interest::sometimes()`][sometimes], and the [`enabled`]
+        /// will receive [`Interest::sometimes()`][sometimes], and the [`enabled`]
         /// method will still be called for that callsite when it records a span or
         /// event.
         ///
diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs
index 17a3775cab..7d631b08e6 100644
--- a/tracing-subscriber/src/registry/sharded.rs
+++ b/tracing-subscriber/src/registry/sharded.rs
@@ -255,7 +255,7 @@ impl Subscriber for Registry {
                 data.filter_map = crate::filter::FILTERING.with(|filtering| filtering.filter_map());
                 #[cfg(debug_assertions)]
                 {
-                    if data.filter_map != FilterMap::default() {
+                    if data.filter_map != FilterMap::new() {
                         debug_assert!(self.has_per_layer_filters());
                     }
                 }
@@ -481,7 +481,7 @@ impl Default for DataInner {
         };
 
         Self {
-            filter_map: FilterMap::default(),
+            filter_map: FilterMap::new(),
             metadata: &NULL_METADATA,
             parent: None,
             ref_count: AtomicUsize::new(0),
@@ -526,7 +526,7 @@ impl Clear for DataInner {
             })
             .clear();
 
-        self.filter_map = FilterMap::default();
+        self.filter_map = FilterMap::new();
     }
 }
 
diff --git a/tracing-subscriber/src/reload.rs b/tracing-subscriber/src/reload.rs
index 096f83d38a..d926c78f5f 100644
--- a/tracing-subscriber/src/reload.rs
+++ b/tracing-subscriber/src/reload.rs
@@ -317,6 +317,16 @@ impl Handle {
         drop(lock);
 
         callsite::rebuild_interest_cache();
+
+        // If the `log` crate compatibility feature is in use, set `log`'s max
+        // level as well, in case the max `tracing` level changed. We do this
+        // *after* rebuilding the interest cache, as that's when the `tracing`
+        // max level filter is re-computed.
+        #[cfg(feature = "tracing-log")]
+        tracing_log::log::set_max_level(tracing_log::AsLog::as_log(
+            &crate::filter::LevelFilter::current(),
+        ));
+
         Ok(())
     }
 
diff --git a/tracing-subscriber/tests/reload.rs b/tracing-subscriber/tests/reload.rs
index 28662e2e6f..8b48cc203b 100644
--- a/tracing-subscriber/tests/reload.rs
+++ b/tracing-subscriber/tests/reload.rs
@@ -32,7 +32,15 @@ impl Subscriber for NopSubscriber {
     fn exit(&self, _: &Id) {}
 }
 
+/// Running these two tests in parallel will cause flaky failures, since they are both modifying the MAX_LEVEL value.
+/// "cargo test -- --test-threads=1 fixes it, but it runs all tests in serial.
+/// The only way to run tests in serial in a single file is this way.
 #[test]
+fn run_all_reload_test() {
+    reload_handle();
+    reload_filter();
+}
+
 fn reload_handle() {
     static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0);
     static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0);
@@ -89,7 +97,6 @@ fn reload_handle() {
     })
 }
 
-#[test]
 fn reload_filter() {
     struct NopLayer;
     impl tracing_subscriber::Layer for NopLayer {
diff --git a/tracing-subscriber/tests/reload_max_log_level.rs b/tracing-subscriber/tests/reload_max_log_level.rs
new file mode 100644
index 0000000000..69230f0013
--- /dev/null
+++ b/tracing-subscriber/tests/reload_max_log_level.rs
@@ -0,0 +1,37 @@
+#![cfg(all(feature = "env-filter", feature = "tracing-log"))]
+
+use tracing::{self, Level};
+use tracing_mock::{expect, subscriber};
+use tracing_subscriber::{filter::LevelFilter, prelude::*, reload};
+
+#[test]
+fn reload_max_log_level() {
+    let (subscriber, finished) = subscriber::mock()
+        .event(expect::event().at_level(Level::INFO))
+        .event(expect::event().at_level(Level::DEBUG))
+        .event(expect::event().at_level(Level::INFO))
+        .only()
+        .run_with_handle();
+    let (filter, reload_handle) = reload::Layer::new(LevelFilter::INFO);
+    subscriber.with(filter).init();
+
+    assert!(log::log_enabled!(log::Level::Info));
+    assert!(!log::log_enabled!(log::Level::Debug));
+    assert!(!log::log_enabled!(log::Level::Trace));
+
+    log::debug!("i'm disabled");
+    log::info!("i'm enabled");
+
+    reload_handle
+        .reload(Level::DEBUG)
+        .expect("reloading succeeds");
+
+    assert!(log::log_enabled!(log::Level::Info));
+    assert!(log::log_enabled!(log::Level::Debug));
+    assert!(!log::log_enabled!(log::Level::Trace));
+
+    log::debug!("i'm enabled now");
+    log::info!("i'm still enabled, too");
+
+    finished.assert_finished();
+}
diff --git a/tracing/CHANGELOG.md b/tracing/CHANGELOG.md
index db1668b16a..ffbd2925f9 100644
--- a/tracing/CHANGELOG.md
+++ b/tracing/CHANGELOG.md
@@ -651,7 +651,7 @@ this release!
   filtering, improving performance when a span or event is disabled by a
   `static_max_level_XXX` feature flag (#868) 
 - `LevelFilter` is now a re-export of the `tracing_core::LevelFilter` type, it
-  can now be used interchangably with the versions in `tracing-core` and
+  can now be used interchangeably with the versions in `tracing-core` and
   `tracing-subscriber` (#853)
 - Significant performance improvements when comparing `LevelFilter`s and
   `Level`s (#853)
diff --git a/tracing/README.md b/tracing/README.md
index c9ab85e104..8585b6e86e 100644
--- a/tracing/README.md
+++ b/tracing/README.md
@@ -145,7 +145,7 @@ use tracing::{debug, error, info, span, warn, Level};
 
 // the `#[tracing::instrument]` attribute creates and enters a span
 // every time the instrumented function is called. The span is named after the
-// the function or method. Paramaters passed to the function are recorded as fields.
+// the function or method. Parameters passed to the function are recorded as fields.
 #[tracing::instrument]
 pub fn shave(yak: usize) -> Result<(), Box> {
     // this creates an event at the DEBUG level with two fields:
@@ -185,7 +185,7 @@ pub fn shave_all(yaks: usize) -> usize {
 
         if let Err(ref error) = res {
             // Like spans, events can also use the field initialization shorthand.
-            // In this instance, `yak` is the field being initalized.
+            // In this instance, `yak` is the field being initialized.
             error!(yak, error = error.as_ref(), "failed to shave yak!");
         } else {
             yaks_shaved += 1;
@@ -397,6 +397,7 @@ maintained by the `tokio` project. These include:
 - [`sentry-tracing`] provides a layer for reporting events and traces to [Sentry].
 - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
 - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
+- [`json-subscriber`] provides a layer for emitting JSON logs. The output can be customized much more than with [`FmtSubscriber`]'s JSON output.
 
 If you're the maintainer of a `tracing` ecosystem crate not listed above,
 please let us know! We'd love to add your project to the list!
@@ -428,6 +429,7 @@ please let us know! We'd love to add your project to the list!
 [`tracing-loki`]: https://crates.io/crates/tracing-loki
 [Grafana Loki]: https://grafana.com/oss/loki/
 [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
+[`json-subscriber`]: https://crates.io/crates/json-subscriber
 
 **Note:** that some of the ecosystem crates are currently unreleased and
 undergoing active development. They may be less stable than `tracing` and
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index b3db75a64d..8b6c7f0b95 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -173,7 +173,7 @@
 //! For functions which don't have built-in tracing support and can't have
 //! the `#[instrument]` attribute applied (such as from an external crate),
 //! the [`Span` struct][`Span`] has a [`in_scope()` method][`in_scope`]
-//! which can be used to easily wrap synchonous code in a span.
+//! which can be used to easily wrap synchronous code in a span.
 //!
 //! For example:
 //! ```rust
@@ -756,6 +756,7 @@
 //!  - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests.
 //!  - [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs.
 //!  - [`clippy-tracing`] provides a tool to add, remove and check for `tracing::instrument`.
+//!  - [`json-subscriber`] provides a subscriber for emitting JSON logs. The output can be customized much more than with [`tracing-subscriber`]'s JSON output.
 //!
 //! If you're the maintainer of a `tracing` ecosystem crate not listed above,
 //! please let us know! We'd love to add your project to the list!
@@ -799,6 +800,7 @@
 //! [`reqwest`]: https://crates.io/crates/reqwest
 //! [`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch
 //! [`clippy-tracing`]: https://crates.io/crates/clippy-tracing
+//! [`json-subscriber`]: https://crates.io/crates/json-subscriber
 //!
 //! 
 //!     Note: Some of these ecosystem crates are currently
@@ -988,7 +990,7 @@ pub mod __macro_support {
     // Re-export the `core` functions that are used in macros. This allows
     // a crate to be named `core` and avoid name clashes.
     // See here: https://github.com/tokio-rs/tracing/issues/2761
-    pub use core::{concat, format_args, iter::Iterator, option::Option};
+    pub use core::{concat, file, format_args, iter::Iterator, line, option::Option};
 
     /// Callsite implementation used by macro-generated code.
     ///
diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index a3115ecf3e..156334a356 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -694,9 +694,9 @@ macro_rules! event {
         static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
             name: $crate::__macro_support::concat!(
                 "event ",
-                file!(),
+                $crate::__macro_support::file!(),
                 ":",
-                line!()
+                $crate::__macro_support::line!()
             ),
             kind: $crate::metadata::Kind::EVENT,
             target: $target,
@@ -855,9 +855,9 @@ macro_rules! event {
         static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
             name: $crate::__macro_support::concat!(
                 "event ",
-                file!(),
+                $crate::__macro_support::file!(),
                 ":",
-                line!()
+                $crate::__macro_support::line!()
             ),
             kind: $crate::metadata::Kind::EVENT,
             target: $target,
@@ -1188,9 +1188,9 @@ macro_rules! enabled {
             static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
                 name: $crate::__macro_support::concat!(
                     "enabled ",
-                    file!(),
+                    $crate::__macro_support::file!(),
                     ":",
-                    line!()
+                    $crate::__macro_support::line!()
                 ),
                 kind: $kind.hint(),
                 target: $target,
@@ -1305,14 +1305,14 @@ macro_rules! trace {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1322,14 +1322,14 @@ macro_rules! trace {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
@@ -1339,14 +1339,14 @@ macro_rules! trace {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1356,14 +1356,14 @@ macro_rules! trace {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1547,7 +1547,6 @@ macro_rules! trace {
         $crate::event!(
             target: module_path!(),
             $crate::Level::TRACE,
-            {},
             $($arg)+
         )
     );
@@ -1582,14 +1581,14 @@ macro_rules! debug {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1599,14 +1598,14 @@ macro_rules! debug {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1616,14 +1615,14 @@ macro_rules! debug {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1633,14 +1632,14 @@ macro_rules! debug {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1824,7 +1823,6 @@ macro_rules! debug {
         $crate::event!(
             target: module_path!(),
             $crate::Level::DEBUG,
-            {},
             $($arg)+
         )
     );
@@ -1870,14 +1868,14 @@ macro_rules! info {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1887,14 +1885,14 @@ macro_rules! info {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
@@ -1904,14 +1902,14 @@ macro_rules! info {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1921,14 +1919,14 @@ macro_rules! info {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -2112,7 +2110,6 @@ macro_rules! info {
         $crate::event!(
             target: module_path!(),
             $crate::Level::INFO,
-            {},
             $($arg)+
         )
     );
@@ -2151,14 +2148,14 @@ macro_rules! warn {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2168,14 +2165,14 @@ macro_rules! warn {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
@@ -2185,14 +2182,14 @@ macro_rules! warn {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2202,14 +2199,14 @@ macro_rules! warn {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2393,7 +2390,6 @@ macro_rules! warn {
         $crate::event!(
             target: module_path!(),
             $crate::Level::WARN,
-            {},
             $($arg)+
         )
     );
@@ -2428,14 +2424,14 @@ macro_rules! error {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2445,14 +2441,14 @@ macro_rules! error {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
@@ -2462,14 +2458,14 @@ macro_rules! error {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2479,14 +2475,14 @@ macro_rules! error {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2670,7 +2666,6 @@ macro_rules! error {
         $crate::event!(
             target: module_path!(),
             $crate::Level::ERROR,
-            {},
             $($arg)+
         )
     );
@@ -3041,7 +3036,7 @@ macro_rules! fieldset {
         $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
     };
 
-    // Remainder is unparseable, but exists --- must be format args!
+    // Remainder is unparsable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* } $($rest:tt)+) => {
         $crate::fieldset!(@ { "message", $($out),*, })
     };
@@ -3071,8 +3066,8 @@ macro_rules! level_to_log {
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __tracing_stringify {
-    ($s:expr) => {
-        stringify!($s)
+    ($($t:tt)*) => {
+        stringify!($($t)*)
     };
 }
 
diff --git a/tracing/tests/enabled.rs b/tracing/tests/enabled.rs
index 5a4596347e..e00b319713 100644
--- a/tracing/tests/enabled.rs
+++ b/tracing/tests/enabled.rs
@@ -43,7 +43,7 @@ fn span_and_event() {
 
     let _guard = tracing::subscriber::set_default(subscriber);
 
-    // Ensure that the `_event` and `_span` alternatives work corretly
+    // Ensure that the `_event` and `_span` alternatives work correctly
     assert!(!tracing::event_enabled!(Level::TRACE));
     assert!(tracing::event_enabled!(Level::DEBUG));
     assert!(tracing::span_enabled!(Level::TRACE));
diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs
index 175b21bedf..25b5bbe0c0 100644
--- a/tracing/tests/event.rs
+++ b/tracing/tests/event.rs
@@ -525,6 +525,12 @@ fn constant_field_name() {
         )
     };
     let (subscriber, handle) = subscriber::mock()
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
         .event(expect_event())
         .event(expect_event())
         .only()
@@ -548,7 +554,67 @@ fn constant_field_name() {
             },
             "quux"
         );
+        tracing::info!(
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "quux"
+        );
+        tracing::info!(
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "quux"
+        );
+        tracing::event!(
+            Level::INFO,
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "{}",
+            "quux"
+        );
+        tracing::event!(
+            Level::INFO,
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "{}",
+            "quux"
+        );
+        tracing::info!(
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "{}",
+            "quux"
+        );
+        tracing::info!(
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "{}",
+            "quux"
+        );
     });
 
     handle.assert_finished();
 }
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn keyword_ident_in_field_name() {
+    let (subscriber, handle) = subscriber::mock()
+        .event(expect::event().with_fields(expect::field("crate").with_value(&"tracing")))
+        .only()
+        .run_with_handle();
+
+    with_default(subscriber, || error!(crate = "tracing", "message"));
+    handle.assert_finished();
+}
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index b6e568f4a6..a072389e24 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -10,6 +10,16 @@ use tracing::{
     span, span_enabled, trace, trace_span, warn, warn_span, Level,
 };
 
+/// A type that implements `Display` and `Debug`, but not `Value`.
+#[derive(Debug)]
+struct DisplayDebug;
+
+impl ::std::fmt::Display for DisplayDebug {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::std::write!(f, "Foo")
+    }
+}
+
 // Tests that macros work across various invocation syntax.
 //
 // These are quite repetitive, and _could_ be generated by a macro. However,
@@ -545,12 +555,15 @@ fn trace() {
     trace!(foo = ?3, bar.baz = %2, quux = false);
     trace!(foo = 3, bar.baz = 2, quux = false);
     trace!(foo = 3, bar.baz = 3,);
+    trace!("foo" = 3, bar.baz = 3,);
+    trace!(foo = 3, "bar.baz" = 3,);
     trace!("foo");
     trace!("foo: {}", 3);
     trace!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     trace!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     trace!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     trace!({ foo = 3, bar.baz = 80 }, "quux");
+    trace!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     trace!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     trace!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     trace!({ foo = 2, bar.baz = 78 }, "quux");
@@ -569,6 +582,9 @@ fn trace() {
     trace!(?foo);
     trace!(%foo);
     trace!(foo);
+    trace!("foo" = ?foo);
+    trace!("foo" = %foo);
+    trace!("foo" = foo);
     trace!(name: "foo", ?foo);
     trace!(name: "foo", %foo);
     trace!(name: "foo", foo);
@@ -581,6 +597,22 @@ fn trace() {
     trace!(target: "foo_events", ?foo, true, "message");
     trace!(target: "foo_events", %foo, true, "message");
     trace!(target: "foo_events", foo, true, "message");
+    trace!(name: "foo", target: "foo_events", ?foo);
+    trace!(name: "foo", target: "foo_events", %foo);
+    trace!(name: "foo", target: "foo_events", foo);
+    let foo = DisplayDebug;
+    trace!(?foo);
+    trace!(%foo);
+    trace!(name: "foo", ?foo);
+    trace!(name: "foo", %foo);
+    trace!(name: "foo", ?foo, true, "message");
+    trace!(name: "foo", %foo, true, "message");
+    trace!(target: "foo_events", ?foo);
+    trace!(target: "foo_events", %foo);
+    trace!(target: "foo_events", ?foo, true, "message");
+    trace!(target: "foo_events", %foo, true, "message");
+    trace!(name: "foo", target: "foo_events", ?foo, true, "message");
+    trace!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -589,12 +621,15 @@ fn debug() {
     debug!(foo = ?3, bar.baz = %2, quux = false);
     debug!(foo = 3, bar.baz = 2, quux = false);
     debug!(foo = 3, bar.baz = 3,);
+    debug!("foo" = 3, bar.baz = 3,);
+    debug!(foo = 3, "bar.baz" = 3,);
     debug!("foo");
     debug!("foo: {}", 3);
     debug!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     debug!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     debug!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     debug!({ foo = 3, bar.baz = 80 }, "quux");
+    debug!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     debug!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     debug!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     debug!({ foo = 2, bar.baz = 78 }, "quux");
@@ -613,6 +648,9 @@ fn debug() {
     debug!(?foo);
     debug!(%foo);
     debug!(foo);
+    debug!("foo" = ?foo);
+    debug!("foo" = %foo);
+    debug!("foo" = foo);
     debug!(name: "foo", ?foo);
     debug!(name: "foo", %foo);
     debug!(name: "foo", foo);
@@ -625,6 +663,22 @@ fn debug() {
     debug!(target: "foo_events", ?foo, true, "message");
     debug!(target: "foo_events", %foo, true, "message");
     debug!(target: "foo_events", foo, true, "message");
+    debug!(name: "foo", target: "foo_events", ?foo);
+    debug!(name: "foo", target: "foo_events", %foo);
+    debug!(name: "foo", target: "foo_events", foo);
+    let foo = DisplayDebug;
+    debug!(?foo);
+    debug!(%foo);
+    debug!(name: "foo", ?foo);
+    debug!(name: "foo", %foo);
+    debug!(name: "foo", ?foo, true, "message");
+    debug!(name: "foo", %foo, true, "message");
+    debug!(target: "foo_events", ?foo);
+    debug!(target: "foo_events", %foo);
+    debug!(target: "foo_events", ?foo, true, "message");
+    debug!(target: "foo_events", %foo, true, "message");
+    debug!(name: "foo", target: "foo_events", ?foo, true, "message");
+    debug!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -633,12 +687,15 @@ fn info() {
     info!(foo = ?3, bar.baz = %2, quux = false);
     info!(foo = 3, bar.baz = 2, quux = false);
     info!(foo = 3, bar.baz = 3,);
+    info!("foo" = 3, bar.baz = 3,);
+    info!(foo = 3, "bar.baz" = 3,);
     info!("foo");
     info!("foo: {}", 3);
     info!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     info!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     info!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     info!({ foo = 3, bar.baz = 80 }, "quux");
+    info!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     info!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     info!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     info!({ foo = 2, bar.baz = 78 }, "quux");
@@ -657,6 +714,9 @@ fn info() {
     info!(?foo);
     info!(%foo);
     info!(foo);
+    info!("foo" = ?foo);
+    info!("foo" = %foo);
+    info!("foo" = foo);
     info!(name: "foo", ?foo);
     info!(name: "foo", %foo);
     info!(name: "foo", foo);
@@ -669,6 +729,22 @@ fn info() {
     info!(target: "foo_events", ?foo, true, "message");
     info!(target: "foo_events", %foo, true, "message");
     info!(target: "foo_events", foo, true, "message");
+    info!(name: "foo", target: "foo_events", ?foo);
+    info!(name: "foo", target: "foo_events", %foo);
+    info!(name: "foo", target: "foo_events", foo);
+    let foo = DisplayDebug;
+    info!(?foo);
+    info!(%foo);
+    info!(name: "foo", ?foo);
+    info!(name: "foo", %foo);
+    info!(name: "foo", ?foo, true, "message");
+    info!(name: "foo", %foo, true, "message");
+    info!(target: "foo_events", ?foo);
+    info!(target: "foo_events", %foo);
+    info!(target: "foo_events", ?foo, true, "message");
+    info!(target: "foo_events", %foo, true, "message");
+    info!(name: "foo", target: "foo_events", ?foo, true, "message");
+    info!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -677,12 +753,15 @@ fn warn() {
     warn!(foo = ?3, bar.baz = %2, quux = false);
     warn!(foo = 3, bar.baz = 2, quux = false);
     warn!(foo = 3, bar.baz = 3,);
+    warn!("foo" = 3, bar.baz = 3,);
+    warn!(foo = 3, "bar.baz" = 3,);
     warn!("foo");
     warn!("foo: {}", 3);
     warn!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     warn!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     warn!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     warn!({ foo = 3, bar.baz = 80 }, "quux");
+    warn!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     warn!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     warn!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     warn!({ foo = 2, bar.baz = 78 }, "quux");
@@ -701,6 +780,9 @@ fn warn() {
     warn!(?foo);
     warn!(%foo);
     warn!(foo);
+    warn!("foo" = ?foo);
+    warn!("foo" = %foo);
+    warn!("foo" = foo);
     warn!(name: "foo", ?foo);
     warn!(name: "foo", %foo);
     warn!(name: "foo", foo);
@@ -713,6 +795,22 @@ fn warn() {
     warn!(target: "foo_events", ?foo, true, "message");
     warn!(target: "foo_events", %foo, true, "message");
     warn!(target: "foo_events", foo, true, "message");
+    warn!(name: "foo", target: "foo_events", ?foo);
+    warn!(name: "foo", target: "foo_events", %foo);
+    warn!(name: "foo", target: "foo_events", foo);
+    let foo = DisplayDebug;
+    warn!(?foo);
+    warn!(%foo);
+    warn!(name: "foo", ?foo);
+    warn!(name: "foo", %foo);
+    warn!(name: "foo", ?foo, true, "message");
+    warn!(name: "foo", %foo, true, "message");
+    warn!(target: "foo_events", ?foo);
+    warn!(target: "foo_events", %foo);
+    warn!(target: "foo_events", ?foo, true, "message");
+    warn!(target: "foo_events", %foo, true, "message");
+    warn!(name: "foo", target: "foo_events", ?foo, true, "message");
+    warn!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -721,15 +819,18 @@ fn error() {
     error!(foo = ?3, bar.baz = %2, quux = false);
     error!(foo = 3, bar.baz = 2, quux = false);
     error!(foo = 3, bar.baz = 3,);
+    error!("foo" = 3, bar.baz = 3,);
+    error!(foo = 3, "bar.baz" = 3,);
     error!("foo");
     error!("foo: {}", 3);
     error!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     error!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     error!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     error!({ foo = 3, bar.baz = 80 }, "quux");
+    error!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     error!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     error!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
-    error!({ foo = 2, bar.baz = 78, }, "quux");
+    error!({ foo = 2, bar.baz = 78 }, "quux");
     error!({ foo = ?2, bar.baz = %78 }, "quux");
     error!(name: "foo", foo = 3, bar.baz = 2, quux = false);
     error!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
@@ -745,6 +846,9 @@ fn error() {
     error!(?foo);
     error!(%foo);
     error!(foo);
+    error!("foo" = ?foo);
+    error!("foo" = %foo);
+    error!("foo" = foo);
     error!(name: "foo", ?foo);
     error!(name: "foo", %foo);
     error!(name: "foo", foo);
@@ -757,6 +861,22 @@ fn error() {
     error!(target: "foo_events", ?foo, true, "message");
     error!(target: "foo_events", %foo, true, "message");
     error!(target: "foo_events", foo, true, "message");
+    error!(name: "foo", target: "foo_events", ?foo);
+    error!(name: "foo", target: "foo_events", %foo);
+    error!(name: "foo", target: "foo_events", foo);
+    let foo = DisplayDebug;
+    error!(?foo);
+    error!(%foo);
+    error!(name: "foo", ?foo);
+    error!(name: "foo", %foo);
+    error!(name: "foo", ?foo, true, "message");
+    error!(name: "foo", %foo, true, "message");
+    error!(target: "foo_events", ?foo);
+    error!(target: "foo_events", %foo);
+    error!(target: "foo_events", ?foo, true, "message");
+    error!(target: "foo_events", %foo, true, "message");
+    error!(name: "foo", target: "foo_events", ?foo, true, "message");
+    error!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs
index bd5bed0d2f..9f4e5c45b8 100644
--- a/tracing/tests/span.rs
+++ b/tracing/tests/span.rs
@@ -6,6 +6,7 @@
 use std::thread;
 
 use tracing::{
+    error_span,
     field::{debug, display},
     subscriber::with_default,
     Level, Span,
@@ -898,3 +899,20 @@ fn constant_field_name() {
 
     handle.assert_finished();
 }
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn keyword_ident_in_field_name_span_macro() {
+    #[derive(Debug)]
+    struct Foo;
+
+    let (subscriber, handle) = subscriber::mock()
+        .new_span(expect::span().with_fields(expect::field("self").with_value(&debug(Foo)).only()))
+        .only()
+        .run_with_handle();
+
+    with_default(subscriber, || {
+        error_span!("span", self = ?Foo);
+    });
+    handle.assert_finished();
+}