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