Skip to content

Commit 5b53bd4

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 9c7033d commit 5b53bd4

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
@@ -64,7 +64,7 @@ pin-project-lite = "0.2.16"
6464
proc-macro2 = { version = "1", default-features = false }
6565
prometheus = { version = "0.14", default-features = false }
6666
prometheus-client = "0.18"
67-
prometools = "0.2.2"
67+
prometools = "0.2.3"
6868
rand = "0.9"
6969
percent-encoding = "2.3"
7070
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 {
@@ -300,6 +301,7 @@ fn metric_init(foundations: &Path, fn_: &ItemFn) -> proc_macro2::TokenStream {
300301
doc,
301302
optional,
302303
ctor,
304+
..
303305
},
304306
ident: field_name,
305307
args,
@@ -357,7 +359,13 @@ fn metric_init(foundations: &Path, fn_: &ItemFn) -> proc_macro2::TokenStream {
357359

358360
fn metric_fn(foundations: &Path, metrics_struct: &Ident, fn_: &ItemFn) -> proc_macro2::TokenStream {
359361
let ItemFn {
360-
attrs: FnAttrs { cfg, doc, .. },
362+
attrs:
363+
FnAttrs {
364+
cfg,
365+
doc,
366+
with_removal,
367+
..
368+
},
361369
fn_token,
362370
vis: fn_vis,
363371
ident: metric_name,
@@ -366,35 +374,77 @@ fn metric_fn(foundations: &Path, metrics_struct: &Ident, fn_: &ItemFn) -> proc_m
366374
ty: metric_type,
367375
} = fn_;
368376

369-
let fn_args = args.iter().map(|arg| arg.to_arg());
377+
let fn_args: Vec<_> = args.iter().map(|arg| arg.to_arg()).collect();
370378

371-
let fn_body = if args.is_empty() {
372-
quote! {
379+
let (convert_args, access_metric) = if args.is_empty() {
380+
let accessor = quote! {
373381
::std::clone::Clone::clone(&#metrics_struct.#metric_name)
374-
}
382+
};
383+
(quote! {}, accessor)
375384
} else {
376385
let label_inits = args.iter().map(|arg| arg.to_initializer());
386+
let convert = quote! {
387+
let __args = #metric_name {
388+
#(#label_inits,)*
389+
};
390+
};
377391

378-
379-
quote! {
392+
let accessor = quote! {
380393
::std::clone::Clone::clone(
381394
&#foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
382395
&#metrics_struct.#metric_name,
383-
&#metric_name {
384-
#(#label_inits,)*
385-
},
396+
&__args,
386397
)
387398
)
399+
};
400+
(convert, accessor)
401+
};
402+
403+
let removal_fns = if cfg!(foundations_unstable) && *with_removal {
404+
let remove_ident = format_ident!("{metric_name}_remove");
405+
let remove_doc = LitStr::new(
406+
&format!("Removes one label set from the `{metric_name}` family."),
407+
Span::call_site(),
408+
);
409+
410+
let clear_ident = format_ident!("{metric_name}_clear");
411+
let clear_doc = LitStr::new(
412+
&format!("Removes all label sets from the `{metric_name}` family."),
413+
Span::call_site(),
414+
);
415+
416+
quote! {
417+
#[doc = #remove_doc]
418+
#(#cfg)*
419+
#fn_vis #fn_token #remove_ident(#(#fn_args,)*) #arrow_token bool {
420+
#convert_args
421+
#foundations::reexports_for_macros::prometools::serde::Family::remove(
422+
&#metrics_struct.#metric_name,
423+
&__args,
424+
)
425+
}
426+
427+
#[doc = #clear_doc]
428+
#(#cfg)*
429+
#fn_vis #fn_token #clear_ident() {
430+
#foundations::reexports_for_macros::prometools::serde::Family::clear(
431+
&#metrics_struct.#metric_name,
432+
)
433+
}
388434
}
435+
} else {
436+
quote! {}
389437
};
390438

391439
quote! {
392440
#[doc = #doc]
393441
#(#cfg)*
394442
#[must_use]
395443
#fn_vis #fn_token #metric_name(#(#fn_args,)*) #arrow_token #metric_type {
396-
#fn_body
444+
#convert_args
445+
#access_metric
397446
}
447+
#removal_fns
398448
}
399449
}
400450

@@ -713,15 +763,16 @@ mod tests {
713763
message: &'static str,
714764
error: impl Into<String>,
715765
) -> Counter {
766+
let __args = connections_errors_total {
767+
endpoint: ::std::clone::Clone::clone(endpoint),
768+
kind,
769+
message,
770+
error: ::std::convert::Into::into(error),
771+
};
716772
::std::clone::Clone::clone(
717773
&::foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
718774
&__oxy_Metrics.connections_errors_total,
719-
&connections_errors_total {
720-
endpoint: ::std::clone::Clone::clone(endpoint),
721-
kind,
722-
message,
723-
error: ::std::convert::Into::into(error),
724-
},
775+
&__args,
725776
)
726777
)
727778
}
@@ -827,15 +878,111 @@ mod tests {
827878
pub fn requests_per_connection(
828879
endpoint: String,
829880
) -> Histogram {
881+
let __args = requests_per_connection { endpoint, };
830882
::std::clone::Clone::clone(
831883
&::foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
832884
&__oxy_Metrics.requests_per_connection,
833-
&requests_per_connection {
834-
endpoint,
885+
&__args,
886+
)
887+
)
888+
}
889+
}
890+
};
891+
892+
assert_eq!(actual, expected);
893+
}
894+
895+
#[cfg(foundations_unstable)]
896+
#[test]
897+
fn expand_with_removal() {
898+
let attr = parse_attr! {
899+
#[metrics]
900+
};
901+
902+
let src = parse_quote! {
903+
pub(crate) mod oxy {
904+
/// Total number of requests
905+
#[with_removal]
906+
pub(crate) fn requests_total(status: u16) -> Counter;
907+
}
908+
};
909+
910+
let actual = expand_from_parsed(attr, src).to_string();
911+
912+
let expected = code_str! {
913+
pub(crate) mod oxy {
914+
use super::*;
915+
916+
#[allow(non_camel_case_types)]
917+
struct __oxy_Metrics {
918+
requests_total:
919+
::foundations::reexports_for_macros::prometools::serde::Family<
920+
requests_total,
921+
Counter,
922+
>,
923+
}
924+
925+
#[allow(non_camel_case_types)]
926+
#[derive(
927+
::std::clone::Clone,
928+
::std::cmp::Eq,
929+
::std::hash::Hash,
930+
::std::cmp::PartialEq,
931+
::foundations::reexports_for_macros::serde::Serialize,
932+
)]
933+
#[serde(crate = ":: foundations :: reexports_for_macros :: serde")]
934+
struct requests_total {
935+
status: u16,
936+
}
937+
938+
#[allow(non_upper_case_globals)]
939+
static __oxy_Metrics: ::foundations::reexports_for_macros::once_cell::sync::Lazy<__oxy_Metrics> =
940+
::foundations::reexports_for_macros::once_cell::sync::Lazy::new(|| {
941+
let registry = &mut *::foundations::telemetry::metrics::internal::Registries::get_subsystem(stringify!(oxy), false, true);
942+
943+
__oxy_Metrics {
944+
requests_total: {
945+
let metric = ::std::default::Default::default();
946+
947+
::foundations::reexports_for_macros::prometheus_client::registry::Registry::register(
948+
registry,
949+
::std::stringify!(requests_total),
950+
str::trim(" Total number of requests"),
951+
::std::boxed::Box::new(::std::clone::Clone::clone(&metric))
952+
);
953+
954+
metric
835955
},
956+
}
957+
});
958+
959+
#[doc = " Total number of requests"]
960+
#[must_use]
961+
pub(crate) fn requests_total(status: u16,) -> Counter {
962+
let __args = requests_total { status, };
963+
::std::clone::Clone::clone(
964+
&::foundations::reexports_for_macros::prometools::serde::Family::get_or_create(
965+
&__oxy_Metrics.requests_total,
966+
&__args,
836967
)
837968
)
838969
}
970+
971+
#[doc = "Removes one label set from the `requests_total` family."]
972+
pub(crate) fn requests_total_remove(status: u16,) -> bool {
973+
let __args = requests_total { status, };
974+
::foundations::reexports_for_macros::prometools::serde::Family::remove(
975+
&__oxy_Metrics.requests_total,
976+
&__args,
977+
)
978+
}
979+
980+
#[doc = "Removes all label sets from the `requests_total` family."]
981+
pub(crate) fn requests_total_clear() {
982+
::foundations::reexports_for_macros::prometools::serde::Family::clear(
983+
&__oxy_Metrics.requests_total,
984+
)
985+
}
839986
}
840987
};
841988

0 commit comments

Comments
 (0)