Repository: autometrics-dev/autometrics-rs (Rust)

Issue:
add some way to skip a function inside of a `#[autometrics]`'d `impl` block
while it is really convenient to just `#[autometrics]` a whole `impl` block, it is not always really *all* blocks one wants to audit. one example in this repo illustrates it very well:

```rust
#[autometrics]
impl Database {
    pub fn new() -> Self {
        Self
    }

    pub async fn load_details(&self) -> Result<(), ApiError> {
        // [...]
    }
}
```

here the `new()` function is very small and the compiler could even inline it, but by adding the `#[autometrics]` macro on top of it it'll add the metrics stuff and thus may even disqualify it from being inline'd and thus be a performance penalty.

something like this would be cool:

```rust
#[autometrics]
impl Database {
    #[skip_autometrics]
    pub fn new() -> Self {
        Self
    }

    pub async fn load_details(&self) -> Result<(), ApiError> {
        // [...]
    }
}
```

# workarounds

currently, it is possible to workaround this by just splitting it into two `impl` blocks:

```rust
impl Database {
    pub fn new() -> Self {
        Self
    }
}

#[autometrics]
impl Database {
    pub async fn load_details(&self) -> Result<(), ApiError> {
        // [...]
    }
}
```

<small>internal ref: [slack chat with evan](https://fiberplane.slack.com/archives/D03HQ2A6LS1/p1675952039427779)</small>



Discussion:
I really like this suggestion.

One thing that's a bit annoying is that I think `skip_autometrics` would need to be a separate proc macro that you'd need to import. The ability for macros to work on additional attributes is [limited to derive macros](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macro-helper-attributes).

A bit of a silly workaround would be to simply call the macro `skip` so if you didn't separately import it, you would add `#[autometrics::skip]` to your function.
Another option would be passing it as a parameter to the autometrics macro `#[autometrics(skip)]`



Patch:
diff --git a/autometrics-macros/src/lib.rs b/autometrics-macros/src/lib.rs
index 3e7c7bd9..b4306fb0 100644
--- a/autometrics-macros/src/lib.rs
+++ b/autometrics-macros/src/lib.rs
@@ -64,6 +64,29 @@ const DEFAULT_PROMETHEUS_URL: &str = "http://localhost:9090";
 /// configured for your histogram. For example, if you want to enforce that a certain percentage of calls
 /// are handled within 200ms, you must have a histogram bucket for 0.2 seconds. If there is no
 /// such bucket, the alert will never fire.
+///
+/// ## Instrumenting `impl` blocks
+///
+/// In addition to instrumenting functions, you can also instrument `impl` blocks.
+///
+/// Example:
+/// ```rust
+/// struct MyStruct;
+///
+/// #[autometrics]
+/// impl MyStruct {
+///     #[skip_autometrics]
+///     pub fn new() -> Self {
+///        Self
+///     }
+///
+///     fn my_function(&self) {
+///        // ...
+///    }
+/// }
+///
+/// This will instrument all functions in the `impl` block, except for those that have the `skip_autometrics` attribute.
+///
 #[proc_macro_attribute]
 pub fn autometrics(
     args: proc_macro::TokenStream,
@@ -205,7 +228,19 @@ fn instrument_impl_block(args: &Args, mut item: ItemImpl) -> Result<TokenStream>
         .items
         .into_iter()
         .map(|item| match item {
-            ImplItem::Method(method) => {
+            ImplItem::Method(mut method) => {
+                // Skip any methods that have the #[skip_autometrics] attribute
+                if method
+                    .attrs
+                    .iter()
+                    .any(|attr| attr.path.is_ident("skip_autometrics"))
+                {
+                    method
+                        .attrs
+                        .retain(|attr| !attr.path.is_ident("skip_autometrics"));
+                    return ImplItem::Method(method);
+                }
+
                 let item_fn = ItemFn {
                     attrs: method.attrs,
                     vis: method.vis,
diff --git a/examples/full-api/src/database.rs b/examples/full-api/src/database.rs
index 806f0fc7..18de2274 100644
--- a/examples/full-api/src/database.rs
+++ b/examples/full-api/src/database.rs
@@ -9,6 +9,7 @@ pub struct Database;
 // You can instrument a whole impl block like this:
 #[autometrics]
 impl Database {
+    #[skip_autometrics]
     pub fn new() -> Self {
         Self
     }


Test patch:
diff --git a/autometrics/tests/integration_test.rs b/autometrics/tests/integration_test.rs
index 5538caf8..621ef64e 100644
--- a/autometrics/tests/integration_test.rs
+++ b/autometrics/tests/integration_test.rs
@@ -45,6 +45,7 @@ pub struct Db {}
 
 #[autometrics]
 impl Db {
+    #[skip_autometrics]
     pub fn new() -> Self {
         Db {}
     }