Skip to content

Commit

Permalink
feat(pruner, metrics): skip attribute for metrics derive macro (#4069)
Browse files Browse the repository at this point in the history
  • Loading branch information
shekhirin authored Aug 9, 2023
1 parent fd7e28e commit e925cbc
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 76 deletions.
196 changes: 130 additions & 66 deletions crates/metrics/metrics-derive/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
use quote::{quote, ToTokens};
use regex::Regex;
use syn::{
punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Lit, LitBool, LitStr,
MetaNameValue, Result, Token,
punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Field, Lit, LitBool, LitStr,
Meta, MetaNameValue, Result, Token,
};

use crate::{metric::Metric, with_attrs::WithAttrs};
Expand All @@ -17,6 +17,19 @@ static METRIC_NAME_RE: Lazy<Regex> =
/// Supported metrics separators
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> {
let ty = &node.ident;
let vis = &node.vis;
Expand All @@ -36,30 +49,49 @@ pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
.iter()
.map(|metric| {
let field_name = &metric.field.ident;
let metric_name =
format!("{}{}{}", scope.value(), metrics_attr.separator(), metric.name());
let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;
Ok((
quote! {
#field_name: #registrar(#metric_name),
},
quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
quote! {
#describe(#metric_name, #description);
},
))
let field_name = &metric.field().ident;
match metric {
MetricField::Included(metric) => {
let metric_name = format!(
"{}{}{}",
scope.value(),
metrics_attr.separator(),
metric.name()
);
let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;
Ok((
quote! {
#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<_>>>()?
.into_iter()
.fold((vec![], vec![], vec![]), |mut acc, x| {
acc.0.push(x.0);
acc.1.push(x.1);
acc.2.push(x.2);
if let Some(describe) = x.2 {
acc.2.push(describe);
}
acc
});

Expand Down Expand Up @@ -93,35 +125,50 @@ pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
.iter()
.map(|metric| {
let name = metric.name();
let separator = metrics_attr.separator();
let metric_name = quote! {
format!("{}{}{}", scope, #separator, #name)
};
let field_name = &metric.field.ident;

let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;

Ok((
quote! {
#field_name: #registrar(#metric_name),
},
quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
quote! {
#describe(#metric_name, #description);
},
))
let field_name = &metric.field().ident;
match metric {
MetricField::Included(metric) => {
let name = metric.name();
let separator = metrics_attr.separator();
let metric_name = quote! {
format!("{}{}{}", scope, #separator, #name)
};

let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;

Ok((
quote! {
#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<_>>>()?
.into_iter()
.fold((vec![], vec![], vec![]), |mut acc, x| {
acc.0.push(x.0);
acc.1.push(x.1);
acc.2.push(x.2);
if let Some(describe) = x.2 {
acc.2.push(describe);
}
acc
});

Expand Down Expand Up @@ -246,40 +293,57 @@ fn parse_metrics_attr(node: &DeriveInput) -> Result<MetricsAttr> {
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 {
return Err(Error::new_spanned(node, "Only structs are supported."))
};

let mut metrics = Vec::with_capacity(data.fields.len());
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")? {
let parsed = metric_attr
.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)?;
for kv in parsed {
let lit = match kv.value {
Expr::Lit(ref expr) => &expr.lit,
_ => continue,
};
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 parsed =
metric_attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in parsed {
match meta {
Meta::Path(path) if path.is_ident("skip") => skip = true,
Meta::NameValue(kv) => {
let lit = match kv.value {
Expr::Lit(ref expr) => &expr.lit,
_ => continue,
};
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."))
}
}
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."))
_ => return Err(Error::new_spanned(meta, "Unsupported attribute entry.")),
}
}
}

if skip {
metrics.push(MetricField::Skipped(field));
continue
}

let description = match describe {
Some(lit_str) => lit_str.value(),
// Parse docs only if `describe` attribute was not provided
Expand All @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions crates/metrics/metrics-derive/tests/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,52 @@ use std::{collections::HashMap, sync::Mutex};
#[derive(Metrics)]
#[metrics(scope = "metrics_custom")]
struct CustomMetrics {
#[metric(skip)]
skipped_field_a: u8,
/// A gauge with doc comment description.
gauge: Gauge,
#[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")]
gauge2: Gauge,
#[metric(skip)]
skipped_field_b: u16,
/// Some doc comment
#[metric(describe = "Metric attribute description will be preferred over doc comment.")]
counter: Counter,
#[metric(skip)]
skipped_field_c: u32,
#[metric(skip)]
skipped_field_d: u64,
/// A renamed histogram.
#[metric(rename = "histogram")]
histo: Histogram,
#[metric(skip)]
skipped_field_e: u128,
}

#[allow(dead_code)]
#[derive(Metrics)]
#[metrics(dynamic = true)]
struct DynamicScopeMetrics {
#[metric(skip)]
skipped_field_a: u8,
/// A gauge with doc comment description.
gauge: Gauge,
#[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")]
gauge2: Gauge,
#[metric(skip)]
skipped_field_b: u16,
/// Some doc comment
#[metric(describe = "Metric attribute description will be preferred over doc comment.")]
counter: Counter,
#[metric(skip)]
skipped_field_c: u32,
#[metric(skip)]
skipped_field_d: u64,
/// A renamed histogram.
#[metric(rename = "histogram")]
histo: Histogram,
#[metric(skip)]
skipped_field_e: u128,
}

static RECORDER: Lazy<TestRecorder> = Lazy::new(TestRecorder::new);
Expand Down
14 changes: 5 additions & 9 deletions crates/prune/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ use reth_metrics::{metrics, metrics::Histogram, Metrics};
use reth_primitives::PrunePart;
use std::collections::HashMap;

#[derive(Debug, Default)]
#[derive(Metrics)]
#[metrics(scope = "pruner")]
pub(crate) struct Metrics {
pub(crate) pruner: PrunerMetrics,
/// Pruning duration
pub(crate) duration_seconds: Histogram,
#[metric(skip)]
prune_parts: HashMap<PrunePart, PrunerPartMetrics>,
}

Expand All @@ -21,13 +24,6 @@ impl Metrics {
}
}

#[derive(Metrics)]
#[metrics(scope = "pruner")]
pub(crate) struct PrunerMetrics {
/// Pruning duration
pub(crate) duration_seconds: Histogram,
}

#[derive(Metrics)]
#[metrics(scope = "pruner.parts")]
pub(crate) struct PrunerPartMetrics {
Expand Down
2 changes: 1 addition & 1 deletion crates/prune/src/pruner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl<DB: Database> Pruner<DB> {
self.last_pruned_block_number = Some(tip_block_number);

let elapsed = start.elapsed();
self.metrics.pruner.duration_seconds.record(elapsed);
self.metrics.duration_seconds.record(elapsed);

trace!(
target: "pruner",
Expand Down

0 comments on commit e925cbc

Please sign in to comment.