πŸ›ˆ 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/
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.63+][msrv]*
10//!
11//! [msrv]: #supported-rust-versions
12//!
13//! ## Usage
14//!
15//! In the `Cargo.toml`:
16//!
17//! ```toml
18//! [dependencies]
19//! tracing = "0.1"
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//! [instrument]: macro@instrument
39//! [span]: https://docs.rs/tracing/latest/tracing/span/index.html
40//!
41//! ## Supported Rust Versions
42//!
43//! Tracing is built against the latest stable release. The minimum supported
44//! version is 1.63. 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/master/assets/logo-type.png",
57    html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico",
58    issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
59)]
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
82use proc_macro2::TokenStream;
83use quote::{quote, ToTokens};
84use syn::parse::{Parse, ParseStream};
85use syn::{Attribute, ItemFn, Signature, Visibility};
86
87mod attr;
88mod expand;
89/// Instruments a function to create and enter a `tracing` [span] every time
90/// the function is called.
91///
92/// Unless overridden, a span with `info` level will be generated.
93/// The generated span's name will be the name of the function.
94/// By default, all arguments to the function are included as fields on the
95/// span. Arguments that are `tracing` [primitive types] implementing the
96/// [`Value` trait] will be recorded as fields of that type. Types which do
97/// not implement `Value` will be recorded using [`std::fmt::Debug`].
98///
99/// [primitive types]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html#foreign-impls
100/// [`Value` trait]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html
101///
102/// To skip recording a function's or method's argument, pass the argument's name
103/// to the `skip` argument on the `#[instrument]` macro. For example,
104/// `skip` can be used when an argument to an instrumented function does
105/// not implement [`fmt::Debug`], or to exclude an argument with a verbose
106/// or costly Debug implementation. Note that:
107/// - multiple argument names can be passed to `skip`.
108/// - arguments passed to `skip` do _not_ need to implement `fmt::Debug`.
109///
110/// Additional fields (key-value pairs with arbitrary data) can be passed to
111/// to the generated span through the `fields` argument on the
112/// `#[instrument]` macro. Strings, integers or boolean literals are accepted values
113/// for each field. The name of the field must be a single valid Rust
114/// identifier, nested (dotted) field names are not supported.
115///
116/// Note that overlap between the names of fields and (non-skipped) arguments
117/// will result in a compile error.
118///
119/// # Examples
120/// Instrumenting a function:
121/// ```
122/// # use tracing_attributes::instrument;
123/// #[instrument]
124/// pub fn my_function(my_arg: usize) {
125///     // This event will be recorded inside a span named `my_function` with the
126///     // field `my_arg`.
127///     tracing::info!("inside my_function!");
128///     // ...
129/// }
130/// ```
131/// Setting the level for the generated span:
132/// ```
133/// # use tracing_attributes::instrument;
134/// # use tracing::Level;
135/// #[instrument(level = Level::DEBUG)]
136/// pub fn my_function() {
137///     // ...
138/// }
139/// ```
140/// Levels can be specified either with [`Level`] constants, literal strings
141/// (e.g., `"debug"`, `"info"`) or numerically (1β€”5, corresponding to [`Level::TRACE`]β€”[`Level::ERROR`]).
142///
143/// Overriding the generated span's name:
144/// ```
145/// # use tracing_attributes::instrument;
146/// #[instrument(name = "my_name")]
147/// pub fn my_function() {
148///     // ...
149/// }
150/// ```
151/// Overriding the generated span's target:
152/// ```
153/// # use tracing_attributes::instrument;
154/// #[instrument(target = "my_target")]
155/// pub fn my_function() {
156///     // ...
157/// }
158/// ```
159/// Overriding the generated span's parent:
160/// ```
161/// # use tracing_attributes::instrument;
162/// #[instrument(parent = None)]
163/// pub fn my_function() {
164///     // ...
165/// }
166/// ```
167/// ```
168/// # use tracing_attributes::instrument;
169/// // A struct which owns a span handle.
170/// struct MyStruct
171/// {
172///     span: tracing::Span
173/// }
174///
175/// impl MyStruct
176/// {
177///     // Use the struct's `span` field as the parent span
178///     #[instrument(parent = &self.span, skip(self))]
179///     fn my_method(&self) {}
180/// }
181/// ```
182/// Specifying [`follows_from`] relationships:
183/// ```
184/// # use tracing_attributes::instrument;
185/// #[instrument(follows_from = causes)]
186/// pub fn my_function(causes: &[tracing::Id]) {
187///     // ...
188/// }
189/// ```
190/// Any expression of type `impl IntoIterator<Item = impl Into<Option<Id>>>`
191/// may be provided to `follows_from`; e.g.:
192/// ```
193/// # use tracing_attributes::instrument;
194/// #[instrument(follows_from = [cause])]
195/// pub fn my_function(cause: &tracing::span::EnteredSpan) {
196///     // ...
197/// }
198/// ```
199///
200///
201/// To skip recording an argument, pass the argument's name to the `skip`:
202///
203/// ```
204/// # use tracing_attributes::instrument;
205/// struct NonDebug;
206///
207/// #[instrument(skip(non_debug))]
208/// fn my_function(arg: usize, non_debug: NonDebug) {
209///     // ...
210/// }
211/// ```
212///
213/// To add additional context to the span, pass key-value pairs to `fields`:
214///
215/// ```
216/// # use tracing_attributes::instrument;
217/// #[instrument(fields(foo="bar", id=1, show=true))]
218/// fn my_function(arg: usize) {
219///     // ...
220/// }
221/// ```
222///
223/// Adding the `ret` argument to `#[instrument]` will emit an event with the function's
224/// return value when the function returns:
225///
226/// ```
227/// # use tracing_attributes::instrument;
228/// #[instrument(ret)]
229/// fn my_function() -> i32 {
230///     42
231/// }
232/// ```
233/// The level of the return value event defaults to the same level as the span generated by `#[instrument]`.
234/// By default, this will be `TRACE`, but if the span level is overridden, the event will be at the same
235/// level.
236///
237/// It's also possible to override the level for the `ret` event independently:
238///
239/// ```
240/// # use tracing_attributes::instrument;
241/// # use tracing::Level;
242/// #[instrument(ret(level = Level::WARN))]
243/// fn my_function() -> i32 {
244///     42
245/// }
246/// ```
247///
248/// **Note**:  if the function returns a `Result<T, E>`, `ret` will record returned values if and
249/// only if the function returns [`Result::Ok`].
250///
251/// By default, returned values will be recorded using their [`std::fmt::Debug`] implementations.
252/// If a returned value implements [`std::fmt::Display`], it can be recorded using its `Display`
253/// implementation instead, by writing `ret(Display)`:
254///
255/// ```
256/// # use tracing_attributes::instrument;
257/// #[instrument(ret(Display))]
258/// fn my_function() -> i32 {
259///     42
260/// }
261/// ```
262///
263/// If the function returns a `Result<T, E>` and `E` implements `std::fmt::Display`, adding
264/// `err` or `err(Display)` will emit error events when the function returns `Err`:
265///
266/// ```
267/// # use tracing_attributes::instrument;
268/// #[instrument(err)]
269/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
270///     Ok(())
271/// }
272/// ```
273///
274/// The level of the error value event defaults to `ERROR`.
275///
276/// Similarly, overriding the level of the `err` event :
277///
278/// ```
279/// # use tracing_attributes::instrument;
280/// # use tracing::Level;
281/// #[instrument(err(level = Level::INFO))]
282/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
283///     Ok(())
284/// }
285/// ```
286///
287/// By default, error values will be recorded using their `std::fmt::Display` implementations.
288/// If an error implements `std::fmt::Debug`, it can be recorded using its `Debug` implementation
289/// instead by writing `err(Debug)`:
290///
291/// ```
292/// # use tracing_attributes::instrument;
293/// #[instrument(err(Debug))]
294/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
295///     Ok(())
296/// }
297/// ```
298///
299/// If a `target` is specified, both the `ret` and `err` arguments will emit outputs to
300/// the declared target (or the default channel if `target` is not specified).
301///
302/// The `ret` and `err` arguments can be combined in order to record an event if a
303/// function returns [`Result::Ok`] or [`Result::Err`]:
304///
305/// ```
306/// # use tracing_attributes::instrument;
307/// #[instrument(err, ret)]
308/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
309///     Ok(())
310/// }
311/// ```
312///
313/// `async fn`s may also be instrumented:
314///
315/// ```
316/// # use tracing_attributes::instrument;
317/// #[instrument]
318/// pub async fn my_function() -> Result<(), ()> {
319///     // ...
320///     # Ok(())
321/// }
322/// ```
323///
324/// It also works with [async-trait](https://crates.io/crates/async-trait)
325/// (a crate that allows defining async functions in traits,
326/// something not currently possible in Rust),
327/// and hopefully most libraries that exhibit similar behaviors:
328///
329/// ```
330/// # use tracing::instrument;
331/// use async_trait::async_trait;
332///
333/// #[async_trait]
334/// pub trait Foo {
335///     async fn foo(&self, arg: usize);
336/// }
337///
338/// #[derive(Debug)]
339/// struct FooImpl(usize);
340///
341/// #[async_trait]
342/// impl Foo for FooImpl {
343///     #[instrument(fields(value = self.0, tmp = std::any::type_name::<Self>()))]
344///     async fn foo(&self, arg: usize) {}
345/// }
346/// ```
347///
348/// `const fn` cannot be instrumented, and will result in a compilation failure:
349///
350/// ```compile_fail
351/// # use tracing_attributes::instrument;
352/// #[instrument]
353/// const fn my_const_function() {}
354/// ```
355///
356/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
357/// [`follows_from`]: https://docs.rs/tracing/latest/tracing/struct.Span.html#method.follows_from
358/// [`tracing`]: https://github.com/tokio-rs/tracing
359/// [`fmt::Debug`]: std::fmt::Debug
360/// [`Level`]: https://docs.rs/tracing/latest/tracing/struct.Level.html
361/// [`Level::TRACE`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.TRACE
362/// [`Level::ERROR`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.ERROR
363#[proc_macro_attribute]
364pub fn instrument(
365    args: proc_macro::TokenStream,
366    item: proc_macro::TokenStream,
367) -> proc_macro::TokenStream {
368    let args = syn::parse_macro_input!(args as attr::InstrumentArgs);
369    // Cloning a `TokenStream` is cheap since it's reference counted internally.
370    instrument_precise(args.clone(), item.clone())
371        .unwrap_or_else(|_err| instrument_speculative(args, item))
372}
373
374/// Instrument the function, without parsing the function body (instead using the raw tokens).
375fn instrument_speculative(
376    args: attr::InstrumentArgs,
377    item: proc_macro::TokenStream,
378) -> proc_macro::TokenStream {
379    let input = syn::parse_macro_input!(item as MaybeItemFn);
380    let instrumented_function_name = input.sig.ident.to_string();
381    expand::gen_function(
382        input.as_ref(),
383        args,
384        instrumented_function_name.as_str(),
385        None,
386    )
387    .into()
388}
389
390/// Instrument the function, by fully parsing the function body,
391/// which allows us to rewrite some statements related to async-like patterns.
392fn instrument_precise(
393    args: attr::InstrumentArgs,
394    item: proc_macro::TokenStream,
395) -> Result<proc_macro::TokenStream, syn::Error> {
396    let input = syn::parse::<ItemFn>(item)?;
397    let instrumented_function_name = input.sig.ident.to_string();
398
399    if input.sig.constness.is_some() {
400        return Ok(quote! {
401            compile_error!("the `#[instrument]` attribute may not be used with `const fn`s")
402        }
403        .into());
404    }
405
406    // check for async_trait-like patterns in the block, and instrument
407    // the future instead of the wrapper
408    if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
409        return async_like.gen_async(args, instrumented_function_name.as_str());
410    }
411
412    let input = MaybeItemFn::from(input);
413
414    Ok(expand::gen_function(
415        input.as_ref(),
416        args,
417        instrumented_function_name.as_str(),
418        None,
419    )
420    .into())
421}
422
423/// This is a more flexible/imprecise `ItemFn` type,
424/// which's block is just a `TokenStream` (it may contain invalid code).
425#[derive(Debug, Clone)]
426struct MaybeItemFn {
427    outer_attrs: Vec<Attribute>,
428    inner_attrs: Vec<Attribute>,
429    vis: Visibility,
430    sig: Signature,
431    block: TokenStream,
432}
433
434impl MaybeItemFn {
435    fn as_ref(&self) -> MaybeItemFnRef<'_, TokenStream> {
436        MaybeItemFnRef {
437            outer_attrs: &self.outer_attrs,
438            inner_attrs: &self.inner_attrs,
439            vis: &self.vis,
440            sig: &self.sig,
441            block: &self.block,
442        }
443    }
444}
445
446/// This parses a `TokenStream` into a `MaybeItemFn`
447/// (just like `ItemFn`, but skips parsing the body).
448impl Parse for MaybeItemFn {
449    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
450        let outer_attrs = input.call(Attribute::parse_outer)?;
451        let vis: Visibility = input.parse()?;
452        let sig: Signature = input.parse()?;
453        let inner_attrs = input.call(Attribute::parse_inner)?;
454        let block: TokenStream = input.parse()?;
455        Ok(Self {
456            outer_attrs,
457            inner_attrs,
458            vis,
459            sig,
460            block,
461        })
462    }
463}
464
465impl From<ItemFn> for MaybeItemFn {
466    fn from(
467        ItemFn {
468            attrs,
469            vis,
470            sig,
471            block,
472        }: ItemFn,
473    ) -> Self {
474        let (outer_attrs, inner_attrs) = attrs
475            .into_iter()
476            .partition(|attr| attr.style == syn::AttrStyle::Outer);
477        Self {
478            outer_attrs,
479            inner_attrs,
480            vis,
481            sig,
482            block: block.to_token_stream(),
483        }
484    }
485}
486
487/// A generic reference type for `MaybeItemFn`,
488/// that takes a generic block type `B` that implements `ToTokens` (eg. `TokenStream`, `Block`).
489#[derive(Debug, Clone)]
490struct MaybeItemFnRef<'a, B: ToTokens> {
491    outer_attrs: &'a Vec<Attribute>,
492    inner_attrs: &'a Vec<Attribute>,
493    vis: &'a Visibility,
494    sig: &'a Signature,
495    block: &'a B,
496}