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