diff --git a/CHANGELOG.md b/CHANGELOG.md index 589134b9..e4234714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - When the `function.calls.duration` histogram is exported to Prometheus, it now includes the units (`function_calls_duration_seconds`) to be in line with Prometheus/OpenMetrics naming conventions. **Dashboards and alerting rules must be updated.** +- Struct methods now have the struct name as part of the `function` label on the metrics + (for example, `MyStruct::my_method`) ### Deprecated @@ -38,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Return types on functions annotated with `#[autometrics]` containing generic +- Return types on functions annotated with `#[autometrics]` containing generic `impl` types in their type arguments (`fn() -> Result`) no longer fail to compile. diff --git a/autometrics-macros/src/lib.rs b/autometrics-macros/src/lib.rs index cfb39a9f..543524dc 100644 --- a/autometrics-macros/src/lib.rs +++ b/autometrics-macros/src/lib.rs @@ -1,7 +1,7 @@ use crate::parse::{AutometricsArgs, Item}; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, ToTokens}; use std::env; use syn::{ parse_macro_input, GenericArgument, ImplItem, ItemFn, ItemImpl, PathArguments, Result, @@ -25,7 +25,7 @@ pub fn autometrics( let item = parse_macro_input!(item as Item); let result = match item { - Item::Function(item) => instrument_function(&args, item), + Item::Function(item) => instrument_function(&args, item, &None), Item::Impl(item) => instrument_impl_block(&args, item), }; @@ -46,12 +46,21 @@ pub fn result_labels(input: proc_macro::TokenStream) -> proc_macro::TokenStream } /// Add autometrics instrumentation to a single function -fn instrument_function(args: &AutometricsArgs, item: ItemFn) -> Result { +fn instrument_function( + args: &AutometricsArgs, + item: ItemFn, + struct_name: &Option, +) -> Result { let sig = item.sig; let block = item.block; let vis = item.vis; let attrs = item.attrs; - let function_name = sig.ident.to_string(); + + // Methods are identified as Struct::method + let function_name = match struct_name { + Some(struct_name) => format!("{}::{}", struct_name, sig.ident.to_string()), + None => sig.ident.to_string(), + }; // The PROMETHEUS_URL can be configured by passing the environment variable during build time let prometheus_url = @@ -295,6 +304,8 @@ fn instrument_function(args: &AutometricsArgs, item: ItemFn) -> Result Result { + let struct_name = Some(item.self_ty.to_token_stream().to_string()); + // Replace all of the method items in place item.items = item .items @@ -319,7 +330,7 @@ fn instrument_impl_block(args: &AutometricsArgs, mut item: ItemImpl) -> Result tokens, Err(err) => err.to_compile_error(), }; diff --git a/autometrics/tests/integration_test.rs b/autometrics/tests/integration_test.rs index e449e494..42339e48 100644 --- a/autometrics/tests/integration_test.rs +++ b/autometrics/tests/integration_test.rs @@ -53,22 +53,22 @@ fn impl_block() { let metrics = prometheus_exporter::encode_to_string().unwrap(); assert!(metrics.lines().any(|line| { line.starts_with("function_calls_total{") - && line.contains(r#"function="test_fn""#) + && line.contains(r#"function="Foo::test_fn""#) && line.ends_with("} 1") })); assert!(metrics.lines().any(|line| line .starts_with("function_calls_duration_seconds_bucket{") - && line.contains(r#"function="test_fn""#) + && line.contains(r#"function="Foo::test_fn""#) && line.ends_with("} 1"))); assert!(metrics.lines().any(|line| { line.starts_with("function_calls_total{") - && line.contains(r#"function="test_method""#) + && line.contains(r#"function="Foo::test_method""#) && line.ends_with("} 1") })); assert!(metrics.lines().any(|line| line .starts_with("function_calls_duration_seconds_bucket{") - && line.contains(r#"function="test_method""#) + && line.contains(r#"function="Foo::test_method""#) && line.ends_with("} 1"))); }