feat(pruner, metrics): skip attribute for metrics derive macro (#4069)

This commit is contained in:
Alexey Shekhirin
2023-08-09 20:50:27 +01:00
committed by GitHub
parent fd7e28e786
commit e925cbcf3a
4 changed files with 154 additions and 74 deletions

View File

@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use regex::Regex; use regex::Regex;
use syn::{ use syn::{
punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Lit, LitBool, LitStr, punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Field, Lit, LitBool, LitStr,
MetaNameValue, Result, Token, Meta, MetaNameValue, Result, Token,
}; };
use crate::{metric::Metric, with_attrs::WithAttrs}; use crate::{metric::Metric, with_attrs::WithAttrs};
@ -17,6 +17,19 @@ static METRIC_NAME_RE: Lazy<Regex> =
/// Supported metrics separators /// Supported metrics separators
const SUPPORTED_SEPARATORS: &[&str] = &[".", "_", ":"]; const SUPPORTED_SEPARATORS: &[&str] = &[".", "_", ":"];
enum MetricField<'a> {
Included(Metric<'a>),
Skipped(&'a Field),
}
impl<'a> MetricField<'a> {
fn field(&self) -> &'a Field {
match self {
MetricField::Included(Metric { field, .. }) | MetricField::Skipped(field) => field,
}
}
}
pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> { pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let ty = &node.ident; let ty = &node.ident;
let vis = &node.vis; let vis = &node.vis;
@ -36,30 +49,49 @@ pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
.iter() .iter()
.map(|metric| { .map(|metric| {
let field_name = &metric.field.ident; let field_name = &metric.field().ident;
let metric_name = match metric {
format!("{}{}{}", scope.value(), metrics_attr.separator(), metric.name()); MetricField::Included(metric) => {
let registrar = metric.register_stmt()?; let metric_name = format!(
let describe = metric.describe_stmt()?; "{}{}{}",
let description = &metric.description; scope.value(),
Ok(( metrics_attr.separator(),
quote! { metric.name()
#field_name: #registrar(#metric_name), );
}, let registrar = metric.register_stmt()?;
quote! { let describe = metric.describe_stmt()?;
#field_name: #registrar(#metric_name, labels.clone()), let description = &metric.description;
}, Ok((
quote! { quote! {
#describe(#metric_name, #description); #field_name: #registrar(#metric_name),
}, },
)) quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
Some(quote! {
#describe(#metric_name, #description);
}),
))
}
MetricField::Skipped(_) => Ok((
quote! {
#field_name: Default::default(),
},
quote! {
#field_name: Default::default(),
},
None,
)),
}
}) })
.collect::<Result<Vec<_>>>()? .collect::<Result<Vec<_>>>()?
.into_iter() .into_iter()
.fold((vec![], vec![], vec![]), |mut acc, x| { .fold((vec![], vec![], vec![]), |mut acc, x| {
acc.0.push(x.0); acc.0.push(x.0);
acc.1.push(x.1); acc.1.push(x.1);
acc.2.push(x.2); if let Some(describe) = x.2 {
acc.2.push(describe);
}
acc acc
}); });
@ -93,35 +125,50 @@ pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
.iter() .iter()
.map(|metric| { .map(|metric| {
let name = metric.name(); let field_name = &metric.field().ident;
let separator = metrics_attr.separator(); match metric {
let metric_name = quote! { MetricField::Included(metric) => {
format!("{}{}{}", scope, #separator, #name) let name = metric.name();
}; let separator = metrics_attr.separator();
let field_name = &metric.field.ident; let metric_name = quote! {
format!("{}{}{}", scope, #separator, #name)
};
let registrar = metric.register_stmt()?; let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?; let describe = metric.describe_stmt()?;
let description = &metric.description; let description = &metric.description;
Ok(( Ok((
quote! { quote! {
#field_name: #registrar(#metric_name), #field_name: #registrar(#metric_name),
}, },
quote! { quote! {
#field_name: #registrar(#metric_name, labels.clone()), #field_name: #registrar(#metric_name, labels.clone()),
}, },
quote! { Some(quote! {
#describe(#metric_name, #description); #describe(#metric_name, #description);
}, }),
)) ))
}
MetricField::Skipped(_) => Ok((
quote! {
#field_name: Default::default(),
},
quote! {
#field_name: Default::default(),
},
None,
)),
}
}) })
.collect::<Result<Vec<_>>>()? .collect::<Result<Vec<_>>>()?
.into_iter() .into_iter()
.fold((vec![], vec![], vec![]), |mut acc, x| { .fold((vec![], vec![], vec![]), |mut acc, x| {
acc.0.push(x.0); acc.0.push(x.0);
acc.1.push(x.1); acc.1.push(x.1);
acc.2.push(x.2); if let Some(describe) = x.2 {
acc.2.push(describe);
}
acc acc
}); });
@ -246,40 +293,57 @@ fn parse_metrics_attr(node: &DeriveInput) -> Result<MetricsAttr> {
Ok(MetricsAttr { scope, separator }) Ok(MetricsAttr { scope, separator })
} }
fn parse_metric_fields(node: &DeriveInput) -> Result<Vec<Metric<'_>>> { fn parse_metric_fields(node: &DeriveInput) -> Result<Vec<MetricField<'_>>> {
let Data::Struct(ref data) = node.data else { let Data::Struct(ref data) = node.data else {
return Err(Error::new_spanned(node, "Only structs are supported.")) return Err(Error::new_spanned(node, "Only structs are supported."))
}; };
let mut metrics = Vec::with_capacity(data.fields.len()); let mut metrics = Vec::with_capacity(data.fields.len());
for field in data.fields.iter() { for field in data.fields.iter() {
let (mut describe, mut rename) = (None, None); let (mut describe, mut rename, mut skip) = (None, None, false);
if let Some(metric_attr) = parse_single_attr(field, "metric")? { if let Some(metric_attr) = parse_single_attr(field, "metric")? {
let parsed = metric_attr let parsed =
.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)?; metric_attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for kv in parsed { for meta in parsed {
let lit = match kv.value { match meta {
Expr::Lit(ref expr) => &expr.lit, Meta::Path(path) if path.is_ident("skip") => skip = true,
_ => continue, Meta::NameValue(kv) => {
}; let lit = match kv.value {
if kv.path.is_ident("describe") { Expr::Lit(ref expr) => &expr.lit,
if describe.is_some() { _ => continue,
return Err(Error::new_spanned(kv, "Duplicate `describe` value provided.")) };
if kv.path.is_ident("describe") {
if describe.is_some() {
return Err(Error::new_spanned(
kv,
"Duplicate `describe` value provided.",
))
}
describe = Some(parse_str_lit(lit)?);
} else if kv.path.is_ident("rename") {
if rename.is_some() {
return Err(Error::new_spanned(
kv,
"Duplicate `rename` value provided.",
))
}
let rename_lit = parse_str_lit(lit)?;
validate_metric_name(&rename_lit)?;
rename = Some(rename_lit)
} else {
return Err(Error::new_spanned(kv, "Unsupported attribute entry."))
}
} }
describe = Some(parse_str_lit(lit)?); _ => return Err(Error::new_spanned(meta, "Unsupported attribute entry.")),
} else if kv.path.is_ident("rename") {
if rename.is_some() {
return Err(Error::new_spanned(kv, "Duplicate `rename` value provided."))
}
let rename_lit = parse_str_lit(lit)?;
validate_metric_name(&rename_lit)?;
rename = Some(rename_lit)
} else {
return Err(Error::new_spanned(kv, "Unsupported attribute entry."))
} }
} }
} }
if skip {
metrics.push(MetricField::Skipped(field));
continue
}
let description = match describe { let description = match describe {
Some(lit_str) => lit_str.value(), Some(lit_str) => lit_str.value(),
// Parse docs only if `describe` attribute was not provided // Parse docs only if `describe` attribute was not provided
@ -294,7 +358,7 @@ fn parse_metric_fields(node: &DeriveInput) -> Result<Vec<Metric<'_>>> {
}, },
}; };
metrics.push(Metric::new(field, description, rename)); metrics.push(MetricField::Included(Metric::new(field, description, rename)));
} }
Ok(metrics) Ok(metrics)

View File

@ -10,32 +10,52 @@ use std::{collections::HashMap, sync::Mutex};
#[derive(Metrics)] #[derive(Metrics)]
#[metrics(scope = "metrics_custom")] #[metrics(scope = "metrics_custom")]
struct CustomMetrics { struct CustomMetrics {
#[metric(skip)]
skipped_field_a: u8,
/// A gauge with doc comment description. /// A gauge with doc comment description.
gauge: Gauge, gauge: Gauge,
#[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")] #[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")]
gauge2: Gauge, gauge2: Gauge,
#[metric(skip)]
skipped_field_b: u16,
/// Some doc comment /// Some doc comment
#[metric(describe = "Metric attribute description will be preferred over doc comment.")] #[metric(describe = "Metric attribute description will be preferred over doc comment.")]
counter: Counter, counter: Counter,
#[metric(skip)]
skipped_field_c: u32,
#[metric(skip)]
skipped_field_d: u64,
/// A renamed histogram. /// A renamed histogram.
#[metric(rename = "histogram")] #[metric(rename = "histogram")]
histo: Histogram, histo: Histogram,
#[metric(skip)]
skipped_field_e: u128,
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Metrics)] #[derive(Metrics)]
#[metrics(dynamic = true)] #[metrics(dynamic = true)]
struct DynamicScopeMetrics { struct DynamicScopeMetrics {
#[metric(skip)]
skipped_field_a: u8,
/// A gauge with doc comment description. /// A gauge with doc comment description.
gauge: Gauge, gauge: Gauge,
#[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")] #[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")]
gauge2: Gauge, gauge2: Gauge,
#[metric(skip)]
skipped_field_b: u16,
/// Some doc comment /// Some doc comment
#[metric(describe = "Metric attribute description will be preferred over doc comment.")] #[metric(describe = "Metric attribute description will be preferred over doc comment.")]
counter: Counter, counter: Counter,
#[metric(skip)]
skipped_field_c: u32,
#[metric(skip)]
skipped_field_d: u64,
/// A renamed histogram. /// A renamed histogram.
#[metric(rename = "histogram")] #[metric(rename = "histogram")]
histo: Histogram, histo: Histogram,
#[metric(skip)]
skipped_field_e: u128,
} }
static RECORDER: Lazy<TestRecorder> = Lazy::new(TestRecorder::new); static RECORDER: Lazy<TestRecorder> = Lazy::new(TestRecorder::new);

View File

@ -2,9 +2,12 @@ use reth_metrics::{metrics, metrics::Histogram, Metrics};
use reth_primitives::PrunePart; use reth_primitives::PrunePart;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug, Default)] #[derive(Metrics)]
#[metrics(scope = "pruner")]
pub(crate) struct Metrics { pub(crate) struct Metrics {
pub(crate) pruner: PrunerMetrics, /// Pruning duration
pub(crate) duration_seconds: Histogram,
#[metric(skip)]
prune_parts: HashMap<PrunePart, PrunerPartMetrics>, prune_parts: HashMap<PrunePart, PrunerPartMetrics>,
} }
@ -21,13 +24,6 @@ impl Metrics {
} }
} }
#[derive(Metrics)]
#[metrics(scope = "pruner")]
pub(crate) struct PrunerMetrics {
/// Pruning duration
pub(crate) duration_seconds: Histogram,
}
#[derive(Metrics)] #[derive(Metrics)]
#[metrics(scope = "pruner.parts")] #[metrics(scope = "pruner.parts")]
pub(crate) struct PrunerPartMetrics { pub(crate) struct PrunerPartMetrics {

View File

@ -150,7 +150,7 @@ impl<DB: Database> Pruner<DB> {
self.last_pruned_block_number = Some(tip_block_number); self.last_pruned_block_number = Some(tip_block_number);
let elapsed = start.elapsed(); let elapsed = start.elapsed();
self.metrics.pruner.duration_seconds.record(elapsed); self.metrics.duration_seconds.record(elapsed);
trace!( trace!(
target: "pruner", target: "pruner",