diff --git a/CHANGELOG.md b/CHANGELOG.md index ff699b6..2764633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ ensure that you match the version that Autometrics uses.** has been improved (#149) - Fixed missing feature flag for `opentelemetry-otlp` when autometrics feature `otel-push-exporter` is enabled +- Fixed incorrect duration being recorded when using `#[async_trait]` together with `#[autometrics]` (#161) + **Please note that the `#[autometrics]` macro needs to be defined BEFORE `#[async_trait]`.** +- Fixed value of the `result` label being empty when the function is annotated with `#[async_trait]` (#161) ### Autometrics 1.0 compliance diff --git a/autometrics-macros/src/lib.rs b/autometrics-macros/src/lib.rs index c90f911..fcf8790 100644 --- a/autometrics-macros/src/lib.rs +++ b/autometrics-macros/src/lib.rs @@ -1,8 +1,9 @@ use crate::parse::{AutometricsArgs, Item}; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use std::env; +use std::str::FromStr; use syn::{ parse_macro_input, GenericArgument, ImplItem, ItemFn, ItemImpl, PathArguments, Result, ReturnType, Type, @@ -21,19 +22,14 @@ pub fn autometrics( args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let args = parse_macro_input!(args as parse::AutometricsArgs); - - // if the input has yet to be modified by the `async-trait` crate, we will fail parsing in - // in the next line, so lets error early now - if let Err(err) = check_async_trait(&item) { - return err.into_compile_error().into(); - } + let args = parse_macro_input!(args as AutometricsArgs); + let (async_trait, item) = check_async_trait(item); let item = parse_macro_input!(item as Item); let result = match item { Item::Function(item) => instrument_function(&args, item, &None), - Item::Impl(item) => instrument_impl_block(&args, item), + Item::Impl(item) => instrument_impl_block(&args, item, async_trait), }; let output = match result { @@ -44,16 +40,21 @@ pub fn autometrics( output.into() } -fn check_async_trait(input: &proc_macro::TokenStream) -> Result<()> { +fn check_async_trait(input: proc_macro::TokenStream) -> (bool, proc_macro::TokenStream) { let str = input.to_string(); if str.contains("#[async_trait]") || str.contains("#[async_trait::async_trait]") { - Err(syn::Error::new( - Span::call_site(), - "#[async_trait] must be defined BEFORE #[autometrics]", - )) + // .unwrap is safe because we only remove tokens from the existing stream, we dont add new ones + ( + true, + proc_macro::TokenStream::from_str( + &str.replace("#[async_trait]", "") + .replace("#[async_trait::async_trait]", ""), + ) + .unwrap(), + ) } else { - Ok(()) + (false, input) } } @@ -331,9 +332,19 @@ fn instrument_function( } /// Add autometrics instrumentation to an entire impl block -fn instrument_impl_block(args: &AutometricsArgs, mut item: ItemImpl) -> Result { +fn instrument_impl_block( + args: &AutometricsArgs, + mut item: ItemImpl, + async_trait: bool, +) -> Result { let struct_name = Some(item.self_ty.to_token_stream().to_string()); + let async_trait = if async_trait { + quote! { #[async_trait::async_trait] } + } else { + quote! {} + }; + // Replace all of the method items in place item.items = item .items @@ -368,7 +379,10 @@ fn instrument_impl_block(args: &AutometricsArgs, mut item: ItemImpl) -> Result tests/result_labels/fail/async_trait_support.rs:15:1 - | -15 | #[autometrics] - | ^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autometrics` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `TestStruct: TestTrait` is not satisfied - --> tests/result_labels/fail/async_trait_support.rs:31:10 - | -31 | ::method().await; - | ^^^^^^^^^^ the trait `TestTrait` is not implemented for `TestStruct` - | -help: this trait has no implementations, consider adding one - --> tests/result_labels/fail/async_trait_support.rs:7:1 - | -7 | trait TestTrait { - | ^^^^^^^^^^^^^^^ - -error[E0599]: no method named `self_method` found for struct `TestStruct` in the current scope - --> tests/result_labels/fail/async_trait_support.rs:32:12 - | -13 | struct TestStruct; - | ----------------- method `self_method` not found for this struct -... -32 | ts.self_method().await; - | ^^^^^^^^^^^ method not found in `TestStruct` - | - = help: items from traits can only be used if the trait is implemented and in scope -note: `TestTrait` defines an item `self_method`, perhaps you need to implement it - --> tests/result_labels/fail/async_trait_support.rs:7:1 - | -7 | trait TestTrait { - | ^^^^^^^^^^^^^^^ diff --git a/autometrics/tests/result_labels/fail/async_trait_support.rs b/autometrics/tests/result_labels/pass/async_trait_support.rs similarity index 100% rename from autometrics/tests/result_labels/fail/async_trait_support.rs rename to autometrics/tests/result_labels/pass/async_trait_support.rs