Skip to content

Commit a018071

Browse files
authored
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 <[email protected]>
1 parent 220f873 commit a018071

File tree

1 file changed

+200
-15
lines changed

1 file changed

+200
-15
lines changed

tracing-attributes/src/lib.rs

Lines changed: 200 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,28 +100,208 @@ use syn::{
100100
/// Instruments a function to create and enter a `tracing` [span] every time
101101
/// the function is called.
102102
///
103-
/// Unless overriden, a span with `info` level will be generated.
104-
/// The generated span's name will be the name of the function. Any arguments
105-
/// to that function will be recorded as fields using [`fmt::Debug`]. To skip
106-
/// recording a function's or method's argument, pass the argument's name
107-
/// to the `skip` argument on the `#[instrument]` macro. For example,
108-
/// `skip` can be used when an argument to an instrumented function does
109-
/// not implement [`fmt::Debug`], or to exclude an argument with a verbose
110-
/// or costly Debug implementation. Note that:
103+
/// By default, the generated span's [name] will be the name of the function,
104+
/// the span's [target] will be the current module path, and the span's [level]
105+
/// will be [`INFO`], although these properties can be overridden. Any arguments
106+
/// to that function will be recorded as fields using [`fmt::Debug`].
107+
///
108+
/// # Overriding Span Attributes
109+
///
110+
/// To change the [name] of the generated span, add a `name` argument to the
111+
/// `#[instrument]` macro, followed by an equals sign and a string literal. For
112+
/// example:
113+
///
114+
/// ```
115+
/// # use tracing_attributes::instrument;
116+
///
117+
/// // The generated span's name will be "my_span" rather than "my_function".
118+
/// #[instrument(name = "my_span")]
119+
/// pub fn my_function() {
120+
/// // ... do something incredibly interesting and important ...
121+
/// }
122+
/// ```
123+
///
124+
/// To override the [target] of the generated span, add a `target` argument to
125+
/// the `#[instrument]` macro, followed by an equals sign and a string literal
126+
/// for the new target. The [module path] is still recorded separately. For
127+
/// example:
128+
///
129+
/// ```
130+
/// pub mod my_module {
131+
/// # use tracing_attributes::instrument;
132+
/// // The generated span's target will be "my_crate::some_special_target",
133+
/// // rather than "my_crate::my_module".
134+
/// #[instrument(target = "my_crate::some_special_target")]
135+
/// pub fn my_function() {
136+
/// // ... all kinds of neat code in here ...
137+
/// }
138+
/// }
139+
/// ```
140+
///
141+
/// Finally, to override the [level] of the generated span, add a `level`
142+
/// argument, followed by an equals sign and a string literal with the name of
143+
/// the desired level. Level names are not case sensitive. For example:
144+
///
145+
/// ```
146+
/// # use tracing_attributes::instrument;
147+
/// // The span's level will be TRACE rather than INFO.
148+
/// #[instrument(level = "trace")]
149+
/// pub fn my_function() {
150+
/// // ... I have written a truly marvelous implementation of this function,
151+
/// // which this example is too narrow to contain ...
152+
/// }
153+
/// ```
154+
///
155+
/// # Skipping Fields
156+
///
157+
/// To skip recording one or more arguments to a function or method, pass
158+
/// the argument's name inside the `skip()` argument on the `#[instrument]`
159+
/// macro. This can be used when an argument to an instrumented function does
160+
/// not implement [`fmt::Debug`], or to exclude an argument with a verbose or
161+
/// costly `Debug` implementation. Note that:
162+
///
111163
/// - multiple argument names can be passed to `skip`.
112164
/// - arguments passed to `skip` do _not_ need to implement `fmt::Debug`.
113165
///
114-
/// You can also pass additional fields (key-value pairs with arbitrary data)
115-
/// to the generated span. This is achieved using the `fields` argument on the
116-
/// `#[instrument]` macro. You can use a string, integer or boolean literal as
117-
/// a value for each field. The name of the field must be a single valid Rust
118-
/// identifier, nested (dotted) field names are not supported.
166+
/// ## Examples
167+
///
168+
/// ```
169+
/// # use tracing_attributes::instrument;
170+
/// // This type doesn't implement `fmt::Debug`!
171+
/// struct NonDebug;
172+
///
173+
/// // `arg` will be recorded, while `non_debug` will not.
174+
/// #[instrument(skip(non_debug))]
175+
/// fn my_function(arg: usize, non_debug: NonDebug) {
176+
/// // ...
177+
/// }
178+
/// ```
179+
///
180+
/// Skipping the `self` parameter:
181+
///
182+
/// ```
183+
/// # use tracing_attributes::instrument;
184+
/// #[derive(Debug)]
185+
/// struct MyType {
186+
/// data: Vec<u8>, // Suppose this buffer is often quite long...
187+
/// }
188+
///
189+
/// impl MyType {
190+
/// // Suppose we don't want to print an entire kilobyte of `data`
191+
/// // every time this is called...
192+
/// #[instrument(skip(self))]
193+
/// pub fn my_method(&mut self, an_interesting_argument: usize) {
194+
/// // ... do something (hopefully, using all that `data`!)
195+
/// }
196+
/// }
197+
/// ```
198+
///
199+
/// # Adding Fields
200+
///
201+
/// Additional fields (key-value pairs with arbitrary data) may be added to the
202+
/// generated span using the `fields` argument on the `#[instrument]` macro. Any
203+
/// Rust expression can be used as a field value in this manner. These
204+
/// expressions will be evaluated at the beginning of the function's body, sso
205+
/// arguments to the function may be used in these expressions. Field names may
206+
/// also be specified *without* values. Doing so will result in an [empty field]
207+
/// whose value may be recorded later within the function body.
119208
///
120209
/// Note that overlap between the names of fields and (non-skipped) arguments
121210
/// will result in a compile error.
122211
///
212+
/// ## Examples
213+
///
214+
/// Adding a new field based on the value of an argument:
215+
///
216+
/// ```
217+
/// # use tracing_attributes::instrument;
218+
///
219+
/// // This will record a field named "i" with the value of `i` *and* a field
220+
/// // named "next" with the value of `i` + 1.
221+
/// #[instrument(fields(next = i + 1))]
222+
/// pub fn my_function(i: usize) {
223+
/// // ...
224+
/// }
225+
/// ```
226+
///
227+
/// Recording specific properties of a struct as their own fields:
228+
///
229+
/// ```
230+
/// # mod http {
231+
/// # pub struct Error;
232+
/// # pub struct Response<B> { pub(super) _b: std::marker::PhantomData<B> }
233+
/// # pub struct Request<B> { _b: B }
234+
/// # impl<B> std::fmt::Debug for Request<B> {
235+
/// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236+
/// # f.pad("request")
237+
/// # }
238+
/// # }
239+
/// # impl<B> Request<B> {
240+
/// # pub fn uri(&self) -> &str { "fake" }
241+
/// # pub fn method(&self) -> &str { "GET" }
242+
/// # }
243+
/// # }
244+
/// # use tracing_attributes::instrument;
245+
///
246+
/// // This will record the request's URI and HTTP method as their own separate
247+
/// // fields.
248+
/// #[instrument(fields(http.uri = req.uri(), http.method = req.method()))]
249+
/// pub fn handle_request<B>(req: http::Request<B>) -> http::Response<B> {
250+
/// // ... handle the request ...
251+
/// # http::Response { _b: std::marker::PhantomData }
252+
/// }
253+
/// ```
254+
///
255+
/// This can be used in conjunction with `skip` to record only some fields of a
256+
/// struct:
257+
/// ```
258+
/// # use tracing_attributes::instrument;
259+
/// // Remember the struct with the very large `data` field from the earlier
260+
/// // example? Now it also has a `name`, which we might want to include in
261+
/// // our span.
262+
/// #[derive(Debug)]
263+
/// struct MyType {
264+
/// name: &'static str,
265+
/// data: Vec<u8>,
266+
/// }
267+
///
268+
/// impl MyType {
269+
/// // This will skip the `data` field, but will include `self.name`,
270+
/// // formatted using `fmt::Display`.
271+
/// #[instrument(skip(self), fields(self.name = %self.name))]
272+
/// pub fn my_method(&mut self, an_interesting_argument: usize) {
273+
/// // ... do something (hopefully, using all that `data`!)
274+
/// }
275+
/// }
276+
/// ```
277+
///
278+
/// Adding an empty field to be recorded later:
279+
///
280+
/// ```
281+
/// # use tracing_attributes::instrument;
282+
///
283+
/// // This function does a very interesting and important mathematical calculation.
284+
/// // Suppose we want to record both the inputs to the calculation *and* its result...
285+
/// #[instrument(fields(result))]
286+
/// pub fn do_calculation(input_1: usize, input_2: usize) -> usize {
287+
/// // Rerform the calculation.
288+
/// let result = input_1 + input_2;
289+
///
290+
/// // Record the result as part of the current span.
291+
/// tracing::Span::current().record("result", &result);
292+
///
293+
/// // Now, the result will also be included on this event!
294+
/// tracing::info!("calculation complete!");
295+
///
296+
/// // ... etc ...
297+
/// # 0
298+
/// }
299+
/// ```
300+
///
123301
/// # Examples
302+
///
124303
/// Instrumenting a function:
304+
///
125305
/// ```
126306
/// # use tracing_attributes::instrument;
127307
/// #[instrument]
@@ -169,7 +349,7 @@ use syn::{
169349
/// }
170350
/// ```
171351
///
172-
/// To add an additional context to the span, you can pass key-value pairs to `fields`:
352+
/// To add an additional context to the span, pass key-value pairs to `fields`:
173353
///
174354
/// ```
175355
/// # use tracing_attributes::instrument;
@@ -251,7 +431,12 @@ use syn::{
251431
/// which you implement the trait: `#[instrument(fields(tmp = std::any::type_name::<Bar>()))]`.
252432
///
253433
/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
254-
/// [`tracing`]: https://github.com/tokio-rs/tracing
434+
/// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name
435+
/// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target
436+
/// [level]: https://docs.rs/tracing/latest/tracing/struct.Level.html
437+
/// [module path]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.module_path
438+
/// [`INFO`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.INFO
439+
/// [empty field]: https://docs.rs/tracing/latest/tracing/field/struct.Empty.html
255440
/// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
256441
#[proc_macro_attribute]
257442
pub fn instrument(

0 commit comments

Comments
 (0)