Skip to content

Commit 492ba79

Browse files
committed
Add #[with_removal] attribute to #[metrics] macro
`#[with_removal]` generates two additional functions that allow to remove individual label sets from a metric family as well as clear the family completely. This is useful when metrics are used for dynamic data, such as peer RTT in a P2P system. The feature is hidden behind `cfg(foundations_unstable)`, which allows us to iterate on the API in the future. Both proc-macro unit tests as well as an integration test are included in the change.
1 parent 0537e58 commit 492ba79

File tree

5 files changed

+257
-61
lines changed

5 files changed

+257
-61
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pin-project-lite = "0.2.16"
6565
proc-macro2 = { version = "1", default-features = false }
6666
prometheus = { version = "0.14", default-features = false }
6767
prometheus-client = "0.18"
68-
prometools = "0.2.2"
68+
prometools = "0.2.3"
6969
rand = "0.9"
7070
percent-encoding = "2.3"
7171
quote = "1"

foundations-macros/src/metrics/mod.rs

Lines changed: 166 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct FnAttrs {
5858
doc: String,
5959
ctor: Option<ExprStruct>,
6060
optional: bool,
61+
with_removal: bool,
6162
}
6263

6364
struct FnArg {
@@ -298,6 +299,7 @@ fn metric_init(foundations: &Path, fn_: &ItemFn) -> proc_macro2::TokenStream {
298299
doc,
299300
optional,
300301
ctor,
302+
..
301303
},
302304
ident: field_name,
303305
args,
@@ -355,7 +357,13 @@ fn metric_init(foundations: &Path, fn_: &ItemFn) -> proc_macro2::TokenStream {
355357

356358
fn metric_fn(foundations: &Path, metrics_struct: &Ident, fn_: &ItemFn) -> proc_macro2::TokenStream {
357359
let ItemFn {
358-
attrs: FnAttrs { cfg, doc, .. },
360+
attrs:
361+
FnAttrs {
362+
cfg,
363+
doc,
364+
with_removal,
365+
..
366+
},
359367
fn_token,
360368
vis: fn_vis,
361369
ident: metric_name,
@@ -364,35 +372,77 @@ fn metric_fn(foundations: &Path, metrics_struct: &Ident, fn_: &ItemFn) -> proc_m
364372
ty: metric_type,
365373
} = fn_;
366374

367-
let fn_args = args.iter().map(|arg| arg.to_arg());
375+
let fn_args: Vec<_> = args.iter().map(|arg| arg.to_arg()).collect();
368376

369-
let fn_body = if args.is_empty() {
370-
quote! {
377+
let (convert_args, access_metric) = if args.is_empty() {
378+
let accessor = quote! {
371379
::std::clone::Clone::clone(&#metrics_struct.#metric_name)
372-
}
380+
};
381+
(quote! {}, accessor)
373382
} else {
374383
let label_inits = args.iter().map(|arg| arg.to_initializer());
384+
let convert = quote! {
385+
let __args = #metric_name {
386+
#(#label_inits,)*
387+
};
388+
};
375389

376-
377-
quote! {
390+
let accessor = quote! {
378391
::std::clone::Clone::clone(
379392
&#foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
380393
&#metrics_struct.#metric_name,
381-
&#metric_name {
382-
#(#label_inits,)*
383-
},
394+
&__args,
384395
)
385396
)
397+
};
398+
(convert, accessor)
399+
};
400+
401+
let removal_fns = if cfg!(foundations_unstable) && *with_removal {
402+
let remove_ident = format_ident!("{metric_name}_remove");
403+
let remove_doc = LitStr::new(
404+
&format!("Removes one label set from the `{metric_name}` family."),
405+
Span::call_site(),
406+
);
407+
408+
let clear_ident = format_ident!("{metric_name}_clear");
409+
let clear_doc = LitStr::new(
410+
&format!("Removes all label sets from the `{metric_name}` family."),
411+
Span::call_site(),
412+
);
413+
414+
quote! {
415+
#[doc = #remove_doc]
416+
#(#cfg)*
417+
#fn_vis #fn_token #remove_ident(#(#fn_args,)*) #arrow_token bool {
418+
#convert_args
419+
#foundations::reexports_for_macros::prometools::serde::Family::remove(
420+
&#metrics_struct.#metric_name,
421+
&__args,
422+
)
423+
}
424+
425+
#[doc = #clear_doc]
426+
#(#cfg)*
427+
#fn_vis #fn_token #clear_ident() {
428+
#foundations::reexports_for_macros::prometools::serde::Family::clear(
429+
&#metrics_struct.#metric_name,
430+
)
431+
}
386432
}
433+
} else {
434+
quote! {}
387435
};
388436

389437
quote! {
390438
#[doc = #doc]
391439
#(#cfg)*
392440
#[must_use]
393441
#fn_vis #fn_token #metric_name(#(#fn_args,)*) #arrow_token #metric_type {
394-
#fn_body
442+
#convert_args
443+
#access_metric
395444
}
445+
#removal_fns
396446
}
397447
}
398448

@@ -711,15 +761,16 @@ mod tests {
711761
message: &'static str,
712762
error: impl Into<String>,
713763
) -> Counter {
764+
let __args = connections_errors_total {
765+
endpoint: ::std::clone::Clone::clone(endpoint),
766+
kind,
767+
message,
768+
error: ::std::convert::Into::into(error),
769+
};
714770
::std::clone::Clone::clone(
715771
&::foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
716772
&__oxy_Metrics.connections_errors_total,
717-
&connections_errors_total {
718-
endpoint: ::std::clone::Clone::clone(endpoint),
719-
kind,
720-
message,
721-
error: ::std::convert::Into::into(error),
722-
},
773+
&__args,
723774
)
724775
)
725776
}
@@ -825,15 +876,111 @@ mod tests {
825876
pub fn requests_per_connection(
826877
endpoint: String,
827878
) -> Histogram {
879+
let __args = requests_per_connection { endpoint, };
828880
::std::clone::Clone::clone(
829881
&::foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
830882
&__oxy_Metrics.requests_per_connection,
831-
&requests_per_connection {
832-
endpoint,
883+
&__args,
884+
)
885+
)
886+
}
887+
}
888+
};
889+
890+
assert_eq!(actual, expected);
891+
}
892+
893+
#[cfg(foundations_unstable)]
894+
#[test]
895+
fn expand_with_removal() {
896+
let attr = parse_attr! {
897+
#[metrics]
898+
};
899+
900+
let src = parse_quote! {
901+
pub(crate) mod oxy {
902+
/// Total number of requests
903+
#[with_removal]
904+
pub(crate) fn requests_total(status: u16) -> Counter;
905+
}
906+
};
907+
908+
let actual = expand_from_parsed(attr, src).to_string();
909+
910+
let expected = code_str! {
911+
pub(crate) mod oxy {
912+
use super::*;
913+
914+
#[allow(non_camel_case_types)]
915+
struct __oxy_Metrics {
916+
requests_total:
917+
::foundations::reexports_for_macros::prometools::serde::Family<
918+
requests_total,
919+
Counter,
920+
>,
921+
}
922+
923+
#[allow(non_camel_case_types)]
924+
#[derive(
925+
::std::clone::Clone,
926+
::std::cmp::Eq,
927+
::std::hash::Hash,
928+
::std::cmp::PartialEq,
929+
::foundations::reexports_for_macros::serde::Serialize,
930+
)]
931+
#[serde(crate = ":: foundations :: reexports_for_macros :: serde")]
932+
struct requests_total {
933+
status: u16,
934+
}
935+
936+
#[allow(non_upper_case_globals)]
937+
static __oxy_Metrics: ::std::sync::LazyLock<__oxy_Metrics> =
938+
::std::sync::LazyLock::new(|| {
939+
let registry = &mut *::foundations::telemetry::metrics::internal::Registries::get_subsystem(stringify!(oxy), false, true);
940+
941+
__oxy_Metrics {
942+
requests_total: {
943+
let metric = ::std::default::Default::default();
944+
945+
::foundations::reexports_for_macros::prometheus_client::registry::Registry::register(
946+
registry,
947+
::std::stringify!(requests_total),
948+
str::trim(" Total number of requests"),
949+
::std::boxed::Box::new(::std::clone::Clone::clone(&metric))
950+
);
951+
952+
metric
833953
},
954+
}
955+
});
956+
957+
#[doc = " Total number of requests"]
958+
#[must_use]
959+
pub(crate) fn requests_total(status: u16,) -> Counter {
960+
let __args = requests_total { status, };
961+
::std::clone::Clone::clone(
962+
&::foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
963+
&__oxy_Metrics.requests_total,
964+
&__args,
834965
)
835966
)
836967
}
968+
969+
#[doc = "Removes one label set from the `requests_total` family."]
970+
pub(crate) fn requests_total_remove(status: u16,) -> bool {
971+
let __args = requests_total { status, };
972+
::foundations::reexports_for_macros::prometools::serde::Family::remove(
973+
&__oxy_Metrics.requests_total,
974+
&__args,
975+
)
976+
}
977+
978+
#[doc = "Removes all label sets from the `requests_total` family."]
979+
pub(crate) fn requests_total_clear() {
980+
::foundations::reexports_for_macros::prometools::serde::Family::clear(
981+
&__oxy_Metrics.requests_total,
982+
)
983+
}
837984
}
838985
};
839986

0 commit comments

Comments
 (0)