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