diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 381f2a511c..2db84963c2 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -39,11 +39,13 @@ //! [instrument]: attr.instrument.html extern crate proc_macro; +use std::collections::HashSet; + use proc_macro::TokenStream; use quote::{quote, quote_spanned, ToTokens}; use syn::{ - spanned::Spanned, AttributeArgs, FnArg, Ident, ItemFn, Lit, LitInt, Meta, MetaNameValue, - NestedMeta, Pat, PatIdent, PatType, Signature, + spanned::Spanned, AttributeArgs, FnArg, Ident, ItemFn, Lit, LitInt, Meta, MetaList, + MetaNameValue, NestedMeta, Pat, PatIdent, PatType, Signature, }; /// Instruments a function to create and enter a `tracing` [span] every time @@ -143,6 +145,12 @@ pub fn instrument(args: TokenStream, item: TokenStream) -> TokenStream { // function name let ident_str = ident.to_string(); + // Pull out the arguments-to-be-skipped first, so we can filter results below. + let skips = match skips(&args) { + Ok(skips) => skips, + Err(err) => return quote!(#err).into(), + }; + let param_names: Vec = params .clone() .into_iter() @@ -153,6 +161,7 @@ pub fn instrument(args: TokenStream, item: TokenStream) -> TokenStream { }, _ => None, }) + .filter(|ident| !skips.contains(ident)) .collect(); let param_names_clone = param_names.clone(); @@ -203,6 +212,36 @@ pub fn instrument(args: TokenStream, item: TokenStream) -> TokenStream { .into() } +fn skips(args: &AttributeArgs) -> Result, impl ToTokens> { + let mut skips = args.iter().filter_map(|arg| match arg { + NestedMeta::Meta(Meta::List(MetaList { + ref path, + ref nested, + .. + })) if path.is_ident("skip") => Some(nested), + _ => None, + }); + let skip = skips.next(); + + // Ensure there's only one skip directive. + if let Some(list) = skips.next() { + return Err(quote_spanned! { + list.span() => compile_error!("expected only a single `skip` argument!") + }); + } + + // Collect the Idents inside the `skip(...)`, if it exists + Ok(skip + .iter() + .map(|list| list.iter()) + .flatten() + .filter_map(|meta| match meta { + NestedMeta::Meta(Meta::Path(p)) => p.get_ident().map(Clone::clone), + _ => None, + }) + .collect()) +} + fn level(args: &AttributeArgs) -> impl ToTokens { let mut levels = args.iter().filter_map(|arg| match arg { NestedMeta::Meta(Meta::NameValue(MetaNameValue { diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs index 569b191643..2045a47f61 100644 --- a/tracing-attributes/tests/instrument.rs +++ b/tracing-attributes/tests/instrument.rs @@ -60,7 +60,8 @@ fn fields() { span.clone().with_field( field::mock("arg1") .with_value(&format_args!("2")) - .and(field::mock("arg2").with_value(&format_args!("false"))), + .and(field::mock("arg2").with_value(&format_args!("false"))) + .only(), ), ) .enter(span.clone()) @@ -70,7 +71,8 @@ fn fields() { span2.clone().with_field( field::mock("arg1") .with_value(&format_args!("3")) - .and(field::mock("arg2").with_value(&format_args!("true"))), + .and(field::mock("arg2").with_value(&format_args!("true"))) + .only(), ), ) .enter(span2.clone()) @@ -87,6 +89,49 @@ fn fields() { handle.assert_finished(); } +#[test] +fn skip() { + struct UnDebug(pub u32); + + #[instrument(target = "my_target", level = "debug", skip(_arg2, _arg3))] + fn my_fn(arg1: usize, _arg2: UnDebug, _arg3: UnDebug) {} + + let span = span::mock() + .named("my_fn") + .at_level(Level::DEBUG) + .with_target("my_target"); + + let span2 = span::mock() + .named("my_fn") + .at_level(Level::DEBUG) + .with_target("my_target"); + let (subscriber, handle) = subscriber::mock() + .new_span( + span.clone() + .with_field(field::mock("arg1").with_value(&format_args!("2")).only()), + ) + .enter(span.clone()) + .exit(span.clone()) + .drop_span(span) + .new_span( + span2 + .clone() + .with_field(field::mock("arg1").with_value(&format_args!("3")).only()), + ) + .enter(span2.clone()) + .exit(span2.clone()) + .drop_span(span2) + .done() + .run_with_handle(); + + with_default(subscriber, || { + my_fn(2, UnDebug(0), UnDebug(1)); + my_fn(3, UnDebug(0), UnDebug(1)); + }); + + handle.assert_finished(); +} + #[test] fn generics() { #[derive(Debug)]