tracing_attributes/
lib.rs

1//! A procedural macro attribute for instrumenting functions with [`tracing`].
2//!
3//! [`tracing`] is a framework for instrumenting Rust programs to collect
4//! structured, event-based diagnostic information. This crate provides the
5//! [`#[instrument]`][instrument] procedural macro attribute.
6//!
7//! Note that this macro is also re-exported by the main `tracing` crate.
8//!
9//! *Compiler support: [requires `rustc` 1.65+][msrv]*
10//!
11//! [msrv]: #supported-rust-versions
12//!
13//! ## Usage
14//!
15//! In the `Cargo.toml`:
16//!
17//! ```toml
18//! [dependencies]
19//! tracing-attributes = "0.1.24"
20//! ```
21//!
22//! The [`#[instrument]`][instrument] attribute can now be added to a function
23//! to automatically create and enter `tracing` [span] when that function is
24//! called. For example:
25//!
26//! ```
27//! use tracing::instrument;
28//!
29//! #[instrument]
30//! pub fn my_function(my_arg: usize) {
31//!     // ...
32//! }
33//!
34//! # fn main() {}
35//! ```
36//!
37//! [`tracing`]: https://crates.io/crates/tracing
38//! [span]: https://docs.rs/tracing/latest/tracing/span/index.html
39//! [instrument]: macro@self::instrument
40//!
41//! ## Supported Rust Versions
42//!
43//! Tracing is built against the latest stable release. The minimum supported
44//! version is 1.65. The current Tracing version is not guaranteed to build on
45//! Rust versions earlier than the minimum supported version.
46//!
47//! Tracing follows the same compiler support policies as the rest of the Tokio
48//! project. The current stable Rust compiler and the three most recent minor
49//! versions before it will always be supported. For example, if the current
50//! stable compiler version is 1.69, the minimum supported version will not be
51//! increased past 1.66, three minor versions prior. Increasing the minimum
52//! supported compiler version is not considered a semver breaking change as
53//! long as doing so complies with this policy.
54//!
55#![doc(
56    html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/main/assets/logo-type.png",
57    issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
58)]
59#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
60#![warn(
61    missing_debug_implementations,
62    missing_docs,
63    rust_2018_idioms,
64    unreachable_pub,
65    bad_style,
66    dead_code,
67    improper_ctypes,
68    non_shorthand_field_patterns,
69    no_mangle_generic_items,
70    overflowing_literals,
71    path_statements,
72    patterns_in_fns_without_body,
73    private_interfaces,
74    private_bounds,
75    unconditional_recursion,
76    unused_allocation,
77    unused_comparisons,
78    unused_parens,
79    while_true
80)]
81// TODO: once `tracing` bumps its MSRV to 1.42, remove this allow.
82#![allow(unused)]
83extern crate proc_macro;
84
85use proc_macro2::TokenStream;
86use quote::TokenStreamExt;
87use quote::{quote, ToTokens};
88use syn::parse::{Parse, ParseStream};
89use syn::token::Brace;
90use syn::{Attribute, ItemFn, Signature, Visibility};
91
92mod attr;
93mod expand;
94/// Instruments a function to create and enter a `tracing` [span] every time
95/// the function is called.
96///
97/// Unless overridden, a span with the [`INFO`] [level] will be generated.
98/// The generated span's name will be the name of the function.
99/// By default, all arguments to the function are included as fields on the
100/// span. Arguments that are `tracing` [primitive types] implementing the
101/// [`Value` trait] will be recorded as fields of that type. Types which do
102/// not implement `Value` will be recorded using [`fmt::Debug`].
103///
104/// [primitive types]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html#foreign-impls
105/// [`Value` trait]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html
106///
107/// # Overriding Span Attributes
108///
109/// To change the [name] of the generated span, add a `name` argument to the
110/// `#[instrument]` macro, followed by an equals sign and a string literal. For
111/// example:
112///
113/// ```
114/// # use tracing_attributes::instrument;
115///
116/// // The generated span's name will be "my_span" rather than "my_function".
117/// #[instrument(name = "my_span")]
118/// pub fn my_function() {
119///     // ... do something incredibly interesting and important ...
120/// }
121/// ```
122///
123/// To override the [target] of the generated span, add a `target` argument to
124/// the `#[instrument]` macro, followed by an equals sign and a string literal
125/// for the new target. The [module path] is still recorded separately. For
126/// example:
127///
128/// ```
129/// pub mod my_module {
130///     # use tracing_attributes::instrument;
131///     // The generated span's target will be "my_crate::some_special_target",
132///     // rather than "my_crate::my_module".
133///     #[instrument(target = "my_crate::some_special_target")]
134///     pub fn my_function() {
135///         // ... all kinds of neat code in here ...
136///     }
137/// }
138/// ```
139///
140/// Finally, to override the [level] of the generated span, add a `level`
141/// argument, followed by an equals sign and a string literal with the name of
142/// the desired level. Level names are not case sensitive. For example:
143///
144/// ```
145/// # use tracing_attributes::instrument;
146/// // The span's level will be TRACE rather than INFO.
147/// #[instrument(level = "trace")]
148/// pub fn my_function() {
149///     // ... I have written a truly marvelous implementation of this function,
150///     // which this example is too narrow to contain ...
151/// }
152/// ```
153///
154/// # Skipping Fields
155///
156/// To skip recording one or more arguments to a function or method, pass
157/// the argument's name inside the `skip()` argument on the `#[instrument]`
158/// macro. This can be used when an argument to an instrumented function does
159/// not implement [`fmt::Debug`], or to exclude an argument with a verbose or
160/// costly `Debug` implementation. Note that:
161///
162/// - multiple argument names can be passed to `skip`.
163/// - arguments passed to `skip` do _not_ need to implement `fmt::Debug`.
164///
165/// You can also use `skip_all` to skip all arguments.
166///
167/// ## Examples
168///
169/// ```
170/// # use tracing_attributes::instrument;
171/// # use std::collections::HashMap;
172/// // This type doesn't implement `fmt::Debug`!
173/// struct NonDebug;
174///
175/// // `arg` will be recorded, while `non_debug` will not.
176/// #[instrument(skip(non_debug))]
177/// fn my_function(arg: usize, non_debug: NonDebug) {
178///     // ...
179/// }
180///
181/// // These arguments are huge
182/// #[instrument(skip_all)]
183/// fn my_big_data_function(large: Vec<u8>, also_large: HashMap<String, String>) {
184///     // ...
185/// }
186/// ```
187///
188/// Skipping the `self` parameter:
189///
190/// ```
191/// # use tracing_attributes::instrument;
192/// #[derive(Debug)]
193/// struct MyType {
194///    data: Vec<u8>, // Suppose this buffer is often quite long...
195/// }
196///
197/// impl MyType {
198///     // Suppose we don't want to print an entire kilobyte of `data`
199///     // every time this is called...
200///     #[instrument(skip(self))]
201///     pub fn my_method(&mut self, an_interesting_argument: usize) {
202///          // ... do something (hopefully, using all that `data`!)
203///     }
204/// }
205/// ```
206///
207/// # Adding Fields
208///
209/// Additional fields (key-value pairs with arbitrary data) can be passed to
210/// to the generated span through the `fields` argument on the
211/// `#[instrument]` macro. Strings, integers or boolean literals are accepted values
212/// for each field. The name of the field must be a single valid Rust
213/// identifier, nested (dotted) field names are not supported. Any
214/// Rust expression can be used as a field value in this manner. These
215/// expressions will be evaluated at the beginning of the function's body, so
216/// arguments to the function may be used in these expressions. Field names may
217/// also be specified *without* values. Doing so will result in an [empty field]
218/// whose value may be recorded later within the function body.
219///
220/// Note that overlap between the names of fields and (non-skipped) arguments
221/// will result in a compile error.
222///
223/// ## Examples
224///
225/// Adding a new field based on the value of an argument:
226///
227/// ```
228/// # use tracing_attributes::instrument;
229///
230/// // This will record a field named "i" with the value of `i` *and* a field
231/// // named "next" with the value of `i` + 1.
232/// #[instrument(fields(next = i + 1))]
233/// pub fn my_function(i: usize) {
234///     // ...
235/// }
236/// ```
237///
238/// Recording specific properties of a struct as their own fields:
239///
240/// ```
241/// # mod http {
242/// #   pub struct Error;
243/// #   pub struct Response<B> { pub(super) _b: std::marker::PhantomData<B> }
244/// #   pub struct Request<B> { _b: B }
245/// #   impl<B> std::fmt::Debug for Request<B> {
246/// #       fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247/// #           f.pad("request")
248/// #       }
249/// #   }
250/// #   impl<B> Request<B> {
251/// #       pub fn uri(&self) -> &str { "fake" }
252/// #       pub fn method(&self) -> &str { "GET" }
253/// #   }
254/// # }
255/// # use tracing_attributes::instrument;
256///
257/// // This will record the request's URI and HTTP method as their own separate
258/// // fields.
259/// #[instrument(fields(http.uri = req.uri(), http.method = req.method()))]
260/// pub fn handle_request<B>(req: http::Request<B>) -> http::Response<B> {
261///     // ... handle the request ...
262///     # http::Response { _b: std::marker::PhantomData }
263/// }
264/// ```
265///
266/// This can be used in conjunction with `skip` or `skip_all` to record only
267/// some fields of a struct:
268/// ```
269/// # use tracing_attributes::instrument;
270/// // Remember the struct with the very large `data` field from the earlier
271/// // example? Now it also has a `name`, which we might want to include in
272/// // our span.
273/// #[derive(Debug)]
274/// struct MyType {
275///    name: &'static str,
276///    data: Vec<u8>,
277/// }
278///
279/// impl MyType {
280///     // This will skip the `data` field, but will include `self.name`,
281///     // formatted using `fmt::Display`.
282///     #[instrument(skip(self), fields(self.name = %self.name))]
283///     pub fn my_method(&mut self, an_interesting_argument: usize) {
284///          // ... do something (hopefully, using all that `data`!)
285///     }
286/// }
287/// ```
288///
289/// Adding an empty field to be recorded later:
290///
291/// ```
292/// # use tracing_attributes::instrument;
293///
294/// // This function does a very interesting and important mathematical calculation.
295/// // Suppose we want to record both the inputs to the calculation *and* its result...
296/// #[instrument(fields(result))]
297/// pub fn do_calculation(input_1: usize, input_2: usize) -> usize {
298///     // Rerform the calculation.
299///     let result = input_1 + input_2;
300///
301///     // Record the result as part of the current span.
302///     tracing::Span::current().record("result", &result);
303///
304///     // Now, the result will also be included on this event!
305///     tracing::info!("calculation complete!");
306///
307///     // ... etc ...
308///     # 0
309/// }
310/// ```
311///
312/// # Examples
313///
314/// Instrumenting a function:
315///
316/// ```
317/// # use tracing_attributes::instrument;
318/// #[instrument]
319/// pub fn my_function(my_arg: usize) {
320///     // This event will be recorded inside a span named `my_function` with the
321///     // field `my_arg`.
322///     tracing::info!("inside my_function!");
323///     // ...
324/// }
325/// ```
326/// Setting the level for the generated span:
327/// ```
328/// # use tracing_attributes::instrument;
329/// # use tracing::Level;
330/// #[instrument(level = Level::DEBUG)]
331/// pub fn my_function() {
332///     // ...
333/// }
334/// ```
335/// Levels can be specified either with [`Level`] constants, literal strings
336/// (e.g., `"debug"`, `"info"`) or numerically (1—5, corresponding to [`Level::TRACE`]—[`Level::ERROR`]).
337///
338/// Overriding the generated span's name:
339/// ```
340/// # use tracing_attributes::instrument;
341/// #[instrument(name = "my_name")]
342/// pub fn my_function() {
343///     // ...
344/// }
345/// ```
346/// Overriding the generated span's target:
347/// ```
348/// # use tracing_attributes::instrument;
349/// #[instrument(target = "my_target")]
350/// pub fn my_function() {
351///     // ...
352/// }
353/// ```
354/// Overriding the generated span's parent:
355/// ```
356/// # use tracing_attributes::instrument;
357/// #[instrument(parent = None)]
358/// pub fn my_function() {
359///     // ...
360/// }
361/// ```
362/// ```
363/// # use tracing_attributes::instrument;
364/// // A struct which owns a span handle.
365/// struct MyStruct
366/// {
367///     span: tracing::Span
368/// }
369///
370/// impl MyStruct
371/// {
372///     // Use the struct's `span` field as the parent span
373///     #[instrument(parent = &self.span, skip(self))]
374///     fn my_method(&self) {}
375/// }
376/// ```
377/// Specifying [`follows_from`] relationships:
378/// ```
379/// # use tracing_attributes::instrument;
380/// #[instrument(follows_from = causes)]
381/// pub fn my_function(causes: &[tracing::Id]) {
382///     // ...
383/// }
384/// ```
385/// Any expression of type `impl IntoIterator<Item = impl Into<Option<Id>>>`
386/// may be provided to `follows_from`; e.g.:
387/// ```
388/// # use tracing_attributes::instrument;
389/// #[instrument(follows_from = [cause])]
390/// pub fn my_function(cause: &tracing::span::EnteredSpan) {
391///     // ...
392/// }
393/// ```
394///
395///
396/// To skip recording an argument, pass the argument's name to the `skip`:
397///
398/// ```
399/// # use tracing_attributes::instrument;
400/// struct NonDebug;
401///
402/// #[instrument(skip(non_debug))]
403/// fn my_function(arg: usize, non_debug: NonDebug) {
404///     // ...
405/// }
406/// ```
407///
408/// To add additional context to the span, pass key-value pairs to `fields`:
409///
410/// ```
411/// # use tracing_attributes::instrument;
412/// #[instrument(fields(foo="bar", id=1, show=true))]
413/// fn my_function(arg: usize) {
414///     // ...
415/// }
416/// ```
417///
418/// Adding the `ret` argument to `#[instrument]` will emit an event with the function's
419/// return value when the function returns:
420///
421/// ```
422/// # use tracing_attributes::instrument;
423/// #[instrument(ret)]
424/// fn my_function() -> i32 {
425///     42
426/// }
427/// ```
428/// The return value event will have the same level as the span generated by `#[instrument]`.
429/// By default, this will be [`INFO`], but if the level is overridden, the event will be at the same
430/// level.
431///
432/// It's also possible to override the level for the `ret` event independently:
433///
434/// ```
435/// # use tracing_attributes::instrument;
436/// # use tracing::Level;
437/// #[instrument(ret(level = Level::WARN))]
438/// fn my_function() -> i32 {
439///     42
440/// }
441/// ```
442///
443/// **Note**:  if the function returns a `Result<T, E>`, `ret` will record returned values if and
444/// only if the function returns [`Result::Ok`].
445///
446/// By default, returned values will be recorded using their [`std::fmt::Debug`] implementations.
447/// If a returned value implements [`std::fmt::Display`], it can be recorded using its `Display`
448/// implementation instead, by writing `ret(Display)`:
449///
450/// ```
451/// # use tracing_attributes::instrument;
452/// #[instrument(ret(Display))]
453/// fn my_function() -> i32 {
454///     42
455/// }
456/// ```
457///
458/// If the function returns a `Result<T, E>` and `E` implements `std::fmt::Display`, adding
459/// `err` or `err(Display)` will emit error events when the function returns `Err`:
460///
461/// ```
462/// # use tracing_attributes::instrument;
463/// #[instrument(err)]
464/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
465///     Ok(())
466/// }
467/// ```
468///
469/// The level of the error value event defaults to `ERROR`.
470///
471/// Similarly, overriding the level of the `err` event :
472///
473/// ```
474/// # use tracing_attributes::instrument;
475/// # use tracing::Level;
476/// #[instrument(err(level = Level::INFO))]
477/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
478///     Ok(())
479/// }
480/// ```
481///
482/// By default, error values will be recorded using their `std::fmt::Display` implementations.
483/// If an error implements `std::fmt::Debug`, it can be recorded using its `Debug` implementation
484/// instead by writing `err(Debug)`:
485///
486/// ```
487/// # use tracing_attributes::instrument;
488/// #[instrument(err(Debug))]
489/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
490///     Ok(())
491/// }
492/// ```
493///
494/// If a `target` is specified, both the `ret` and `err` arguments will emit outputs to
495/// the declared target (or the default channel if `target` is not specified).
496///
497/// The `ret` and `err` arguments can be combined in order to record an event if a
498/// function returns [`Result::Ok`] or [`Result::Err`]:
499///
500/// ```
501/// # use tracing_attributes::instrument;
502/// #[instrument(err, ret)]
503/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
504///     Ok(())
505/// }
506/// ```
507///
508/// `async fn`s may also be instrumented:
509///
510/// ```
511/// # use tracing_attributes::instrument;
512/// #[instrument]
513/// pub async fn my_function() -> Result<(), ()> {
514///     // ...
515///     # Ok(())
516/// }
517/// ```
518///
519/// It also works with [async-trait](https://crates.io/crates/async-trait)
520/// (a crate that allows defining async functions in traits,
521/// something not currently possible in Rust),
522/// and hopefully most libraries that exhibit similar behaviors:
523///
524/// ```
525/// # use tracing::instrument;
526/// use async_trait::async_trait;
527///
528/// #[async_trait]
529/// pub trait Foo {
530///     async fn foo(&self, arg: usize);
531/// }
532///
533/// #[derive(Debug)]
534/// struct FooImpl(usize);
535///
536/// #[async_trait]
537/// impl Foo for FooImpl {
538///     #[instrument(fields(value = self.0, tmp = std::any::type_name::<Self>()))]
539///     async fn foo(&self, arg: usize) {}
540/// }
541/// ```
542///
543/// `const fn` cannot be instrumented, and will result in a compilation failure:
544///
545/// ```compile_fail
546/// # use tracing_attributes::instrument;
547/// #[instrument]
548/// const fn my_const_function() {}
549/// ```
550///
551/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
552/// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name
553/// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target
554/// [level]: https://docs.rs/tracing/latest/tracing/struct.Level.html
555/// [module path]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.module_path
556/// [`INFO`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.INFO
557/// [empty field]: https://docs.rs/tracing/latest/tracing/field/struct.Empty.html
558/// [field syntax]: https://docs.rs/tracing/latest/tracing/#recording-fields
559/// [`follows_from`]: https://docs.rs/tracing/latest/tracing/struct.Span.html#method.follows_from
560/// [`tracing`]: https://github.com/tokio-rs/tracing
561/// [`fmt::Debug`]: std::fmt::Debug
562/// [`Level`]: https://docs.rs/tracing/latest/tracing/struct.Level.html
563/// [`Level::TRACE`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.TRACE
564/// [`Level::ERROR`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.ERROR
565#[proc_macro_attribute]
566pub fn instrument(
567    args: proc_macro::TokenStream,
568    item: proc_macro::TokenStream,
569) -> proc_macro::TokenStream {
570    let args = syn::parse_macro_input!(args as attr::InstrumentArgs);
571    // Cloning a `TokenStream` is cheap since it's reference counted internally.
572    instrument_precise(args.clone(), item.clone())
573        .unwrap_or_else(|_err| instrument_speculative(args, item))
574}
575
576/// Instrument the function, without parsing the function body (instead using the raw tokens).
577fn instrument_speculative(
578    args: attr::InstrumentArgs,
579    item: proc_macro::TokenStream,
580) -> proc_macro::TokenStream {
581    let input = syn::parse_macro_input!(item as MaybeItemFn);
582    let instrumented_function_name = input.sig.ident.to_string();
583    expand::gen_function(
584        input.as_ref(),
585        args,
586        instrumented_function_name.as_str(),
587        None,
588    )
589    .into()
590}
591
592/// Instrument the function, by fully parsing the function body,
593/// which allows us to rewrite some statements related to async-like patterns.
594fn instrument_precise(
595    args: attr::InstrumentArgs,
596    item: proc_macro::TokenStream,
597) -> Result<proc_macro::TokenStream, syn::Error> {
598    let input = syn::parse::<ItemFn>(item)?;
599    let instrumented_function_name = input.sig.ident.to_string();
600
601    if input.sig.constness.is_some() {
602        return Ok(quote! {
603            compile_error!("the `#[instrument]` attribute may not be used with `const fn`s")
604        }
605        .into());
606    }
607
608    // check for async_trait-like patterns in the block, and instrument
609    // the future instead of the wrapper
610    if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
611        return async_like.gen_async(args, instrumented_function_name.as_str());
612    }
613
614    let input = MaybeItemFn::from(input);
615
616    Ok(expand::gen_function(
617        input.as_ref(),
618        args,
619        instrumented_function_name.as_str(),
620        None,
621    )
622    .into())
623}
624
625/// This is a more flexible/imprecise `ItemFn` type,
626/// which's block is just a `TokenStream` (it may contain invalid code).
627#[derive(Debug, Clone)]
628struct MaybeItemFn {
629    outer_attrs: Vec<Attribute>,
630    inner_attrs: Vec<Attribute>,
631    vis: Visibility,
632    sig: Signature,
633    brace_token: Brace,
634    block: TokenStream,
635}
636
637impl MaybeItemFn {
638    fn as_ref(&self) -> MaybeItemFnRef<'_, TokenStream> {
639        MaybeItemFnRef {
640            outer_attrs: &self.outer_attrs,
641            inner_attrs: &self.inner_attrs,
642            vis: &self.vis,
643            sig: &self.sig,
644            brace_token: &self.brace_token,
645            block: &self.block,
646        }
647    }
648}
649
650/// This parses a `TokenStream` into a `MaybeItemFn`
651/// (just like `ItemFn`, but skips parsing the body).
652impl Parse for MaybeItemFn {
653    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
654        let outer_attrs = input.call(Attribute::parse_outer)?;
655        let vis: Visibility = input.parse()?;
656        let sig: Signature = input.parse()?;
657        let inner_attrs = input.call(Attribute::parse_inner)?;
658        let block;
659        let brace_token = syn::braced!(block in input);
660        let block: TokenStream = block.call(|buffer| buffer.parse())?;
661        Ok(Self {
662            outer_attrs,
663            inner_attrs,
664            vis,
665            sig,
666            brace_token,
667            block,
668        })
669    }
670}
671
672impl From<ItemFn> for MaybeItemFn {
673    fn from(
674        ItemFn {
675            attrs,
676            vis,
677            sig,
678            block,
679        }: ItemFn,
680    ) -> Self {
681        let (outer_attrs, inner_attrs) = attrs
682            .into_iter()
683            .partition(|attr| attr.style == syn::AttrStyle::Outer);
684        let mut block_tokens = TokenStream::new();
685        block_tokens.append_all(block.stmts);
686        Self {
687            outer_attrs,
688            inner_attrs,
689            vis,
690            sig,
691            brace_token: block.brace_token,
692            block: block_tokens,
693        }
694    }
695}
696
697/// A generic reference type for `MaybeItemFn`,
698/// that takes a generic block type `B` that implements `ToTokens` (eg. `TokenStream`, `Block`).
699#[derive(Debug, Clone)]
700struct MaybeItemFnRef<'a, B: ToTokens> {
701    outer_attrs: &'a Vec<Attribute>,
702    inner_attrs: &'a Vec<Attribute>,
703    vis: &'a Visibility,
704    sig: &'a Signature,
705    brace_token: &'a Brace,
706    block: &'a B,
707}