Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 7 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Comment on lines +309 to +313
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple

};
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