From a0180711fd299c693be2718602e57d84954aaeb5 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 2 Feb 2021 15:28:47 -0800 Subject: [PATCH] attributes: update & improve docs (#1215) The `tracing-attributes` documentation for `instrument` is out of date and could use some improvement. In particular, it doesn't mention that empty fields can be created in `instrument`'s `fields` argument, and states that dotted fields are forbidden and that field values must be literals, both of which are no longer the case. See also #1210 and #1211. This branch updates the docs to correct the false statements, and also fleshes out the usage examples a bit. I wasn't sure what to do with the old examples --- some of them are now redundant, but it's also useful to have a big list of examples for every allowed form. Any thoughts? Signed-off-by: Eliza Weisman --- tracing-attributes/src/lib.rs | 215 +++++++++++++++++++++++++++++++--- 1 file changed, 200 insertions(+), 15 deletions(-) diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 7cffaf6d..ca74ea8b 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -100,28 +100,208 @@ use syn::{ /// Instruments a function to create and enter a `tracing` [span] every time /// the function is called. /// -/// Unless overriden, a span with `info` level will be generated. -/// The generated span's name will be the name of the function. Any arguments -/// to that function will be recorded as fields using [`fmt::Debug`]. To skip -/// recording a function's or method's argument, pass the argument's name -/// to the `skip` argument on the `#[instrument]` macro. For example, -/// `skip` can be used when an argument to an instrumented function does -/// not implement [`fmt::Debug`], or to exclude an argument with a verbose -/// or costly Debug implementation. Note that: +/// By default, the generated span's [name] will be the name of the function, +/// the span's [target] will be the current module path, and the span's [level] +/// will be [`INFO`], although these properties can be overridden. Any arguments +/// to that function will be recorded as fields using [`fmt::Debug`]. +/// +/// # Overriding Span Attributes +/// +/// To change the [name] of the generated span, add a `name` argument to the +/// `#[instrument]` macro, followed by an equals sign and a string literal. For +/// example: +/// +/// ``` +/// # use tracing_attributes::instrument; +/// +/// // The generated span's name will be "my_span" rather than "my_function". +/// #[instrument(name = "my_span")] +/// pub fn my_function() { +/// // ... do something incredibly interesting and important ... +/// } +/// ``` +/// +/// To override the [target] of the generated span, add a `target` argument to +/// the `#[instrument]` macro, followed by an equals sign and a string literal +/// for the new target. The [module path] is still recorded separately. For +/// example: +/// +/// ``` +/// pub mod my_module { +/// # use tracing_attributes::instrument; +/// // The generated span's target will be "my_crate::some_special_target", +/// // rather than "my_crate::my_module". +/// #[instrument(target = "my_crate::some_special_target")] +/// pub fn my_function() { +/// // ... all kinds of neat code in here ... +/// } +/// } +/// ``` +/// +/// Finally, to override the [level] of the generated span, add a `level` +/// argument, followed by an equals sign and a string literal with the name of +/// the desired level. Level names are not case sensitive. For example: +/// +/// ``` +/// # use tracing_attributes::instrument; +/// // The span's level will be TRACE rather than INFO. +/// #[instrument(level = "trace")] +/// pub fn my_function() { +/// // ... I have written a truly marvelous implementation of this function, +/// // which this example is too narrow to contain ... +/// } +/// ``` +/// +/// # Skipping Fields +/// +/// To skip recording one or more arguments to a function or method, pass +/// the argument's name inside the `skip()` argument on the `#[instrument]` +/// macro. This can be used when an argument to an instrumented function does +/// not implement [`fmt::Debug`], or to exclude an argument with a verbose or +/// costly `Debug` implementation. Note that: +/// /// - multiple argument names can be passed to `skip`. /// - arguments passed to `skip` do _not_ need to implement `fmt::Debug`. /// -/// You can also pass additional fields (key-value pairs with arbitrary data) -/// to the generated span. This is achieved using the `fields` argument on the -/// `#[instrument]` macro. You can use a string, integer or boolean literal as -/// a value for each field. The name of the field must be a single valid Rust -/// identifier, nested (dotted) field names are not supported. +/// ## Examples +/// +/// ``` +/// # use tracing_attributes::instrument; +/// // This type doesn't implement `fmt::Debug`! +/// struct NonDebug; +/// +/// // `arg` will be recorded, while `non_debug` will not. +/// #[instrument(skip(non_debug))] +/// fn my_function(arg: usize, non_debug: NonDebug) { +/// // ... +/// } +/// ``` +/// +/// Skipping the `self` parameter: +/// +/// ``` +/// # use tracing_attributes::instrument; +/// #[derive(Debug)] +/// struct MyType { +/// data: Vec, // Suppose this buffer is often quite long... +/// } +/// +/// impl MyType { +/// // Suppose we don't want to print an entire kilobyte of `data` +/// // every time this is called... +/// #[instrument(skip(self))] +/// pub fn my_method(&mut self, an_interesting_argument: usize) { +/// // ... do something (hopefully, using all that `data`!) +/// } +/// } +/// ``` +/// +/// # Adding Fields +/// +/// Additional fields (key-value pairs with arbitrary data) may be added to the +/// generated span using the `fields` argument on the `#[instrument]` macro. Any +/// Rust expression can be used as a field value in this manner. These +/// expressions will be evaluated at the beginning of the function's body, sso +/// arguments to the function may be used in these expressions. Field names may +/// also be specified *without* values. Doing so will result in an [empty field] +/// whose value may be recorded later within the function body. /// /// Note that overlap between the names of fields and (non-skipped) arguments /// will result in a compile error. /// +/// ## Examples +/// +/// Adding a new field based on the value of an argument: +/// +/// ``` +/// # use tracing_attributes::instrument; +/// +/// // This will record a field named "i" with the value of `i` *and* a field +/// // named "next" with the value of `i` + 1. +/// #[instrument(fields(next = i + 1))] +/// pub fn my_function(i: usize) { +/// // ... +/// } +/// ``` +/// +/// Recording specific properties of a struct as their own fields: +/// +/// ``` +/// # mod http { +/// # pub struct Error; +/// # pub struct Response { pub(super) _b: std::marker::PhantomData } +/// # pub struct Request { _b: B } +/// # impl std::fmt::Debug for Request { +/// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// # f.pad("request") +/// # } +/// # } +/// # impl Request { +/// # pub fn uri(&self) -> &str { "fake" } +/// # pub fn method(&self) -> &str { "GET" } +/// # } +/// # } +/// # use tracing_attributes::instrument; +/// +/// // This will record the request's URI and HTTP method as their own separate +/// // fields. +/// #[instrument(fields(http.uri = req.uri(), http.method = req.method()))] +/// pub fn handle_request(req: http::Request) -> http::Response { +/// // ... handle the request ... +/// # http::Response { _b: std::marker::PhantomData } +/// } +/// ``` +/// +/// This can be used in conjunction with `skip` to record only some fields of a +/// struct: +/// ``` +/// # use tracing_attributes::instrument; +/// // Remember the struct with the very large `data` field from the earlier +/// // example? Now it also has a `name`, which we might want to include in +/// // our span. +/// #[derive(Debug)] +/// struct MyType { +/// name: &'static str, +/// data: Vec, +/// } +/// +/// impl MyType { +/// // This will skip the `data` field, but will include `self.name`, +/// // formatted using `fmt::Display`. +/// #[instrument(skip(self), fields(self.name = %self.name))] +/// pub fn my_method(&mut self, an_interesting_argument: usize) { +/// // ... do something (hopefully, using all that `data`!) +/// } +/// } +/// ``` +/// +/// Adding an empty field to be recorded later: +/// +/// ``` +/// # use tracing_attributes::instrument; +/// +/// // This function does a very interesting and important mathematical calculation. +/// // Suppose we want to record both the inputs to the calculation *and* its result... +/// #[instrument(fields(result))] +/// pub fn do_calculation(input_1: usize, input_2: usize) -> usize { +/// // Rerform the calculation. +/// let result = input_1 + input_2; +/// +/// // Record the result as part of the current span. +/// tracing::Span::current().record("result", &result); +/// +/// // Now, the result will also be included on this event! +/// tracing::info!("calculation complete!"); +/// +/// // ... etc ... +/// # 0 +/// } +/// ``` +/// /// # Examples +/// /// Instrumenting a function: +/// /// ``` /// # use tracing_attributes::instrument; /// #[instrument] @@ -169,7 +349,7 @@ use syn::{ /// } /// ``` /// -/// To add an additional context to the span, you can pass key-value pairs to `fields`: +/// To add an additional context to the span, pass key-value pairs to `fields`: /// /// ``` /// # use tracing_attributes::instrument; @@ -251,7 +431,12 @@ use syn::{ /// which you implement the trait: `#[instrument(fields(tmp = std::any::type_name::()))]`. /// /// [span]: https://docs.rs/tracing/latest/tracing/span/index.html -/// [`tracing`]: https://github.com/tokio-rs/tracing +/// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name +/// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target +/// [level]: https://docs.rs/tracing/latest/tracing/struct.Level.html +/// [module path]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.module_path +/// [`INFO`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.INFO +/// [empty field]: https://docs.rs/tracing/latest/tracing/field/struct.Empty.html /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html #[proc_macro_attribute] pub fn instrument(