🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_attributes/
attr.rs

1use std::collections::HashSet;
2use syn::{punctuated::Punctuated, Expr, Ident, LitInt, LitStr, Path, Token};
3
4use proc_macro2::TokenStream;
5use quote::{quote, quote_spanned, ToTokens};
6use syn::ext::IdentExt as _;
7use syn::parse::{Parse, ParseStream};
8
9/// Arguments to `#[instrument(err(...))]` and `#[instrument(ret(...))]` which describe how the
10/// return value event should be emitted.
11#[derive(Clone, Default, Debug)]
12pub(crate) struct EventArgs {
13    level: Option<Level>,
14    pub(crate) mode: FormatMode,
15}
16
17#[derive(Clone, Default, Debug)]
18pub(crate) struct InstrumentArgs {
19    level: Option<Level>,
20    pub(crate) name: Option<LitStrOrIdent>,
21    target: Option<LitStrOrIdent>,
22    pub(crate) parent: Option<Expr>,
23    pub(crate) follows_from: Option<Expr>,
24    pub(crate) skips: HashSet<Ident>,
25    pub(crate) fields: Option<Fields>,
26    pub(crate) err_args: Option<EventArgs>,
27    pub(crate) ret_args: Option<EventArgs>,
28    /// Errors describing any unrecognized parse inputs that we skipped.
29    parse_warnings: Vec<syn::Error>,
30}
31
32impl InstrumentArgs {
33    pub(crate) fn level(&self) -> Level {
34        self.level.clone().unwrap_or(Level::Info)
35    }
36
37    pub(crate) fn target(&self) -> impl ToTokens {
38        if let Some(ref target) = self.target {
39            quote!(#target)
40        } else {
41            quote!(module_path!())
42        }
43    }
44
45    /// Generate "deprecation" warnings for any unrecognized attribute inputs
46    /// that we skipped.
47    ///
48    /// For backwards compatibility, we need to emit compiler warnings rather
49    /// than errors for unrecognized inputs. Generating a fake deprecation is
50    /// the only way to do this on stable Rust right now.
51    pub(crate) fn warnings(&self) -> impl ToTokens {
52        let warnings = self.parse_warnings.iter().map(|err| {
53            let msg = format!("found unrecognized input, {}", err);
54            let msg = LitStr::new(&msg, err.span());
55            // TODO(eliza): This is a bit of a hack, but it's just about the
56            // only way to emit warnings from a proc macro on stable Rust.
57            // Eventually, when the `proc_macro::Diagnostic` API stabilizes, we
58            // should definitely use that instead.
59            quote_spanned! {err.span()=>
60                #[warn(deprecated)]
61                {
62                    #[deprecated(since = "not actually deprecated", note = #msg)]
63                    const TRACING_INSTRUMENT_WARNING: () = ();
64                    let _ = TRACING_INSTRUMENT_WARNING;
65                }
66            }
67        });
68        quote! {
69            { #(#warnings)* }
70        }
71    }
72}
73
74impl Parse for InstrumentArgs {
75    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
76        let mut args = Self::default();
77        while !input.is_empty() {
78            let lookahead = input.lookahead1();
79            if lookahead.peek(kw::name) {
80                if args.name.is_some() {
81                    return Err(input.error("expected only a single `name` argument"));
82                }
83                let name = input.parse::<StrArg<kw::name>>()?.value;
84                args.name = Some(name);
85            } else if lookahead.peek(LitStr) {
86                // XXX: apparently we support names as either named args with an
87                // sign, _or_ as unnamed string literals. That's weird, but
88                // changing it is apparently breaking.
89                // This also means that when using idents for name, it must be via
90                // a named arg, i.e. `#[instrument(name = SOME_IDENT)]`.
91                if args.name.is_some() {
92                    return Err(input.error("expected only a single `name` argument"));
93                }
94                args.name = Some(input.parse()?);
95            } else if lookahead.peek(kw::target) {
96                if args.target.is_some() {
97                    return Err(input.error("expected only a single `target` argument"));
98                }
99                let target = input.parse::<StrArg<kw::target>>()?.value;
100                args.target = Some(target);
101            } else if lookahead.peek(kw::parent) {
102                if args.target.is_some() {
103                    return Err(input.error("expected only a single `parent` argument"));
104                }
105                let parent = input.parse::<ExprArg<kw::parent>>()?;
106                args.parent = Some(parent.value);
107            } else if lookahead.peek(kw::follows_from) {
108                if args.target.is_some() {
109                    return Err(input.error("expected only a single `follows_from` argument"));
110                }
111                let follows_from = input.parse::<ExprArg<kw::follows_from>>()?;
112                args.follows_from = Some(follows_from.value);
113            } else if lookahead.peek(kw::level) {
114                if args.level.is_some() {
115                    return Err(input.error("expected only a single `level` argument"));
116                }
117                args.level = Some(input.parse()?);
118            } else if lookahead.peek(kw::skip) {
119                if !args.skips.is_empty() {
120                    return Err(input.error("expected only a single `skip` argument"));
121                }
122                let Skips(skips) = input.parse()?;
123                args.skips = skips;
124            } else if lookahead.peek(kw::fields) {
125                if args.fields.is_some() {
126                    return Err(input.error("expected only a single `fields` argument"));
127                }
128                args.fields = Some(input.parse()?);
129            } else if lookahead.peek(kw::err) {
130                let _ = input.parse::<kw::err>();
131                let err_args = EventArgs::parse(input)?;
132                args.err_args = Some(err_args);
133            } else if lookahead.peek(kw::ret) {
134                let _ = input.parse::<kw::ret>()?;
135                let ret_args = EventArgs::parse(input)?;
136                args.ret_args = Some(ret_args);
137            } else if lookahead.peek(Token![,]) {
138                let _ = input.parse::<Token![,]>()?;
139            } else {
140                // We found a token that we didn't expect!
141                // We want to emit warnings for these, rather than errors, so
142                // we'll add it to the list of unrecognized inputs we've seen so
143                // far and keep going.
144                args.parse_warnings.push(lookahead.error());
145                // Parse the unrecognized token tree to advance the parse
146                // stream, and throw it away so we can keep parsing.
147                let _ = input.parse::<proc_macro2::TokenTree>();
148            }
149        }
150        Ok(args)
151    }
152}
153
154impl EventArgs {
155    pub(crate) fn level(&self, default: Level) -> Level {
156        self.level.clone().unwrap_or(default)
157    }
158}
159
160impl Parse for EventArgs {
161    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
162        if !input.peek(syn::token::Paren) {
163            return Ok(Self::default());
164        }
165        let content;
166        let _ = syn::parenthesized!(content in input);
167        let mut result = Self::default();
168        let mut parse_one_arg =
169            || {
170                let lookahead = content.lookahead1();
171                if lookahead.peek(kw::level) {
172                    if result.level.is_some() {
173                        return Err(content.error("expected only a single `level` argument"));
174                    }
175                    result.level = Some(content.parse()?);
176                } else if result.mode != FormatMode::default() {
177                    return Err(content.error("expected only a single format argument"));
178                } else if let Some(ident) = content.parse::<Option<Ident>>()? {
179                    match ident.to_string().as_str() {
180                        "Debug" => result.mode = FormatMode::Debug,
181                        "Display" => result.mode = FormatMode::Display,
182                        _ => return Err(syn::Error::new(
183                            ident.span(),
184                            "unknown event formatting mode, expected either `Debug` or `Display`",
185                        )),
186                    }
187                }
188                Ok(())
189            };
190        parse_one_arg()?;
191        if !content.is_empty() {
192            if content.lookahead1().peek(Token![,]) {
193                let _ = content.parse::<Token![,]>()?;
194                parse_one_arg()?;
195            } else {
196                return Err(content.error("expected `,` or `)`"));
197            }
198        }
199        Ok(result)
200    }
201}
202
203#[derive(Debug, Clone)]
204pub(super) enum LitStrOrIdent {
205    LitStr(LitStr),
206    Ident(Ident),
207}
208
209impl ToTokens for LitStrOrIdent {
210    fn to_tokens(&self, tokens: &mut TokenStream) {
211        match self {
212            LitStrOrIdent::LitStr(target) => target.to_tokens(tokens),
213            LitStrOrIdent::Ident(ident) => ident.to_tokens(tokens),
214        }
215    }
216}
217
218impl Parse for LitStrOrIdent {
219    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
220        input
221            .parse::<LitStr>()
222            .map(LitStrOrIdent::LitStr)
223            .or_else(|_| input.parse::<Ident>().map(LitStrOrIdent::Ident))
224    }
225}
226
227struct StrArg<T> {
228    value: LitStrOrIdent,
229    _p: std::marker::PhantomData<T>,
230}
231
232impl<T: Parse> Parse for StrArg<T> {
233    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
234        let _ = input.parse::<T>()?;
235        let _ = input.parse::<Token![=]>()?;
236        let value = input.parse()?;
237        Ok(Self {
238            value,
239            _p: std::marker::PhantomData,
240        })
241    }
242}
243
244struct ExprArg<T> {
245    value: Expr,
246    _p: std::marker::PhantomData<T>,
247}
248
249impl<T: Parse> Parse for ExprArg<T> {
250    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
251        let _ = input.parse::<T>()?;
252        let _ = input.parse::<Token![=]>()?;
253        let value = input.parse()?;
254        Ok(Self {
255            value,
256            _p: std::marker::PhantomData,
257        })
258    }
259}
260
261struct Skips(HashSet<Ident>);
262
263impl Parse for Skips {
264    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
265        let _ = input.parse::<kw::skip>();
266        let content;
267        let _ = syn::parenthesized!(content in input);
268        let names = content.parse_terminated(Ident::parse_any, Token![,])?;
269        let mut skips = HashSet::new();
270        for name in names {
271            if skips.contains(&name) {
272                return Err(syn::Error::new(
273                    name.span(),
274                    "tried to skip the same field twice",
275                ));
276            } else {
277                skips.insert(name);
278            }
279        }
280        Ok(Self(skips))
281    }
282}
283
284#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
285pub(crate) enum FormatMode {
286    #[default]
287    Default,
288    Display,
289    Debug,
290}
291
292#[derive(Clone, Debug)]
293pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>);
294
295#[derive(Clone, Debug)]
296pub(crate) struct Field {
297    pub(crate) name: Punctuated<Ident, Token![.]>,
298    pub(crate) value: Option<Expr>,
299    pub(crate) kind: FieldKind,
300}
301
302#[derive(Clone, Debug, Eq, PartialEq)]
303pub(crate) enum FieldKind {
304    Debug,
305    Display,
306    Value,
307}
308
309impl Parse for Fields {
310    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
311        let _ = input.parse::<kw::fields>();
312        let content;
313        let _ = syn::parenthesized!(content in input);
314        let fields = content.parse_terminated(Field::parse, Token![,])?;
315        Ok(Self(fields))
316    }
317}
318
319impl ToTokens for Fields {
320    fn to_tokens(&self, tokens: &mut TokenStream) {
321        self.0.to_tokens(tokens)
322    }
323}
324
325impl Parse for Field {
326    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
327        let mut kind = FieldKind::Value;
328        if input.peek(Token![%]) {
329            input.parse::<Token![%]>()?;
330            kind = FieldKind::Display;
331        } else if input.peek(Token![?]) {
332            input.parse::<Token![?]>()?;
333            kind = FieldKind::Debug;
334        };
335        let name = Punctuated::parse_separated_nonempty_with(input, Ident::parse_any)?;
336        let value = if input.peek(Token![=]) {
337            input.parse::<Token![=]>()?;
338            if input.peek(Token![%]) {
339                input.parse::<Token![%]>()?;
340                kind = FieldKind::Display;
341            } else if input.peek(Token![?]) {
342                input.parse::<Token![?]>()?;
343                kind = FieldKind::Debug;
344            };
345            Some(input.parse()?)
346        } else {
347            None
348        };
349        Ok(Self { name, value, kind })
350    }
351}
352
353impl ToTokens for Field {
354    fn to_tokens(&self, tokens: &mut TokenStream) {
355        if let Some(ref value) = self.value {
356            let name = &self.name;
357            let kind = &self.kind;
358            tokens.extend(quote! {
359                #name = #kind #value
360            })
361        } else if self.kind == FieldKind::Value {
362            // XXX(eliza): I don't like that fields without values produce
363            // empty fields rather than local variable shorthand...but,
364            // we've released a version where field names without values in
365            // `instrument` produce empty field values, so changing it now
366            // is a breaking change. agh.
367            let name = &self.name;
368            tokens.extend(quote!(#name = tracing::field::Empty))
369        } else {
370            self.kind.to_tokens(tokens);
371            self.name.to_tokens(tokens);
372        }
373    }
374}
375
376impl ToTokens for FieldKind {
377    fn to_tokens(&self, tokens: &mut TokenStream) {
378        match self {
379            FieldKind::Debug => tokens.extend(quote! { ? }),
380            FieldKind::Display => tokens.extend(quote! { % }),
381            _ => {}
382        }
383    }
384}
385
386#[derive(Clone, Debug)]
387pub(crate) enum Level {
388    Trace,
389    Debug,
390    Info,
391    Warn,
392    Error,
393    Path(Path),
394}
395
396impl Parse for Level {
397    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
398        let _ = input.parse::<kw::level>()?;
399        let _ = input.parse::<Token![=]>()?;
400        let lookahead = input.lookahead1();
401        if lookahead.peek(LitStr) {
402            let str: LitStr = input.parse()?;
403            match str.value() {
404                s if s.eq_ignore_ascii_case("trace") => Ok(Level::Trace),
405                s if s.eq_ignore_ascii_case("debug") => Ok(Level::Debug),
406                s if s.eq_ignore_ascii_case("info") => Ok(Level::Info),
407                s if s.eq_ignore_ascii_case("warn") => Ok(Level::Warn),
408                s if s.eq_ignore_ascii_case("error") => Ok(Level::Error),
409                _ => Err(input.error(
410                    "unknown verbosity level, expected one of \"trace\", \
411                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
412                )),
413            }
414        } else if lookahead.peek(LitInt) {
415            fn is_level(lit: &LitInt, expected: u64) -> bool {
416                match lit.base10_parse::<u64>() {
417                    Ok(value) => value == expected,
418                    Err(_) => false,
419                }
420            }
421            let int: LitInt = input.parse()?;
422            match &int {
423                i if is_level(i, 1) => Ok(Level::Trace),
424                i if is_level(i, 2) => Ok(Level::Debug),
425                i if is_level(i, 3) => Ok(Level::Info),
426                i if is_level(i, 4) => Ok(Level::Warn),
427                i if is_level(i, 5) => Ok(Level::Error),
428                _ => Err(input.error(
429                    "unknown verbosity level, expected one of \"trace\", \
430                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
431                )),
432            }
433        } else if lookahead.peek(Ident) {
434            Ok(Self::Path(input.parse()?))
435        } else {
436            Err(lookahead.error())
437        }
438    }
439}
440
441impl ToTokens for Level {
442    fn to_tokens(&self, tokens: &mut TokenStream) {
443        match self {
444            Level::Trace => tokens.extend(quote!(tracing::Level::TRACE)),
445            Level::Debug => tokens.extend(quote!(tracing::Level::DEBUG)),
446            Level::Info => tokens.extend(quote!(tracing::Level::INFO)),
447            Level::Warn => tokens.extend(quote!(tracing::Level::WARN)),
448            Level::Error => tokens.extend(quote!(tracing::Level::ERROR)),
449            Level::Path(ref pat) => tokens.extend(quote!(#pat)),
450        }
451    }
452}
453
454mod kw {
455    syn::custom_keyword!(fields);
456    syn::custom_keyword!(skip);
457    syn::custom_keyword!(level);
458    syn::custom_keyword!(target);
459    syn::custom_keyword!(parent);
460    syn::custom_keyword!(follows_from);
461    syn::custom_keyword!(name);
462    syn::custom_keyword!(err);
463    syn::custom_keyword!(ret);
464}