mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
deps: remove reth-metrics-derive for metrics-derive (#11335)
This commit is contained in:
50
Cargo.lock
generated
50
Cargo.lock
generated
@ -4646,6 +4646,18 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metrics-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3dbdd96ed57d565ec744cba02862d707acf373c5772d152abae6ec5c4e24f6c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metrics-exporter-prometheus"
|
name = "metrics-exporter-prometheus"
|
||||||
version = "0.15.3"
|
version = "0.15.3"
|
||||||
@ -7497,24 +7509,11 @@ version = "1.0.7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"metrics",
|
"metrics",
|
||||||
"reth-metrics-derive",
|
"metrics-derive",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "reth-metrics-derive"
|
|
||||||
version = "1.0.7"
|
|
||||||
dependencies = [
|
|
||||||
"metrics",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"regex",
|
|
||||||
"serial_test",
|
|
||||||
"syn 2.0.79",
|
|
||||||
"trybuild",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reth-net-banlist"
|
name = "reth-net-banlist"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@ -10222,15 +10221,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termcolor"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termtree"
|
name = "termtree"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -10860,20 +10850,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "trybuild"
|
|
||||||
version = "1.0.99"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8"
|
|
||||||
dependencies = [
|
|
||||||
"glob",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
"termcolor",
|
|
||||||
"toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.23.0"
|
version = "0.23.0"
|
||||||
|
|||||||
@ -48,7 +48,6 @@ members = [
|
|||||||
"crates/exex/test-utils/",
|
"crates/exex/test-utils/",
|
||||||
"crates/exex/types/",
|
"crates/exex/types/",
|
||||||
"crates/metrics/",
|
"crates/metrics/",
|
||||||
"crates/metrics/metrics-derive/",
|
|
||||||
"crates/net/banlist/",
|
"crates/net/banlist/",
|
||||||
"crates/net/discv4/",
|
"crates/net/discv4/",
|
||||||
"crates/net/discv5/",
|
"crates/net/discv5/",
|
||||||
@ -345,7 +344,6 @@ reth-ipc = { path = "crates/rpc/ipc" }
|
|||||||
reth-libmdbx = { path = "crates/storage/libmdbx-rs" }
|
reth-libmdbx = { path = "crates/storage/libmdbx-rs" }
|
||||||
reth-mdbx-sys = { path = "crates/storage/libmdbx-rs/mdbx-sys" }
|
reth-mdbx-sys = { path = "crates/storage/libmdbx-rs/mdbx-sys" }
|
||||||
reth-metrics = { path = "crates/metrics" }
|
reth-metrics = { path = "crates/metrics" }
|
||||||
reth-metrics-derive = { path = "crates/metrics/metrics-derive" }
|
|
||||||
reth-net-banlist = { path = "crates/net/banlist" }
|
reth-net-banlist = { path = "crates/net/banlist" }
|
||||||
reth-net-nat = { path = "crates/net/nat" }
|
reth-net-nat = { path = "crates/net/nat" }
|
||||||
reth-network = { path = "crates/net/network" }
|
reth-network = { path = "crates/net/network" }
|
||||||
@ -517,6 +515,7 @@ zstd = "0.13"
|
|||||||
|
|
||||||
# metrics
|
# metrics
|
||||||
metrics = "0.23.0"
|
metrics = "0.23.0"
|
||||||
|
metrics-derive = "0.1"
|
||||||
metrics-exporter-prometheus = { version = "0.15.0", default-features = false }
|
metrics-exporter-prometheus = { version = "0.15.0", default-features = false }
|
||||||
metrics-process = "2.1.0"
|
metrics-process = "2.1.0"
|
||||||
metrics-util = { default-features = false, version = "0.17.0" }
|
metrics-util = { default-features = false, version = "0.17.0" }
|
||||||
|
|||||||
@ -12,11 +12,9 @@ description = "reth metrics utilities"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# reth
|
|
||||||
reth-metrics-derive.workspace = true
|
|
||||||
|
|
||||||
# metrics
|
# metrics
|
||||||
metrics.workspace = true
|
metrics.workspace = true
|
||||||
|
metrics-derive.workspace = true
|
||||||
|
|
||||||
# async
|
# async
|
||||||
tokio = { workspace = true, features = ["full"], optional = true }
|
tokio = { workspace = true, features = ["full"], optional = true }
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "reth-metrics-derive"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro2.workspace = true
|
|
||||||
syn = { workspace = true, features = ["extra-traits"] }
|
|
||||||
quote.workspace = true
|
|
||||||
regex = "1.6.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
metrics.workspace = true
|
|
||||||
serial_test.workspace = true
|
|
||||||
trybuild = "1.0"
|
|
||||||
@ -1,436 +0,0 @@
|
|||||||
use quote::{quote, ToTokens};
|
|
||||||
use regex::Regex;
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
use syn::{
|
|
||||||
punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Field, Lit, LitBool, LitStr,
|
|
||||||
Meta, MetaNameValue, Result, Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{metric::Metric, with_attrs::WithAttrs};
|
|
||||||
|
|
||||||
/// Metric name regex according to Prometheus data model
|
|
||||||
///
|
|
||||||
/// See <https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels>
|
|
||||||
static METRIC_NAME_RE: LazyLock<Regex> =
|
|
||||||
LazyLock::new(|| Regex::new(r"^[a-zA-Z_:.][a-zA-Z0-9_:.]*$").unwrap());
|
|
||||||
|
|
||||||
/// Supported metrics separators
|
|
||||||
const SUPPORTED_SEPARATORS: &[&str] = &[".", "_", ":"];
|
|
||||||
|
|
||||||
enum MetricField<'a> {
|
|
||||||
Included(Metric<'a>),
|
|
||||||
Skipped(&'a Field),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MetricField<'a> {
|
|
||||||
const 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;
|
|
||||||
let ident_name = ty.to_string();
|
|
||||||
|
|
||||||
let metrics_attr = parse_metrics_attr(node)?;
|
|
||||||
let metric_fields = parse_metric_fields(node)?;
|
|
||||||
|
|
||||||
let describe_doc = quote! {
|
|
||||||
/// Describe all exposed metrics. Internally calls `describe_*` macros from
|
|
||||||
/// the metrics crate according to the metric type.
|
|
||||||
///
|
|
||||||
/// See <https://docs.rs/metrics/0.20.1/metrics/index.html#macros>
|
|
||||||
};
|
|
||||||
let register_and_describe = match &metrics_attr.scope {
|
|
||||||
MetricsScope::Static(scope) => {
|
|
||||||
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
|
|
||||||
.iter()
|
|
||||||
.map(|metric| {
|
|
||||||
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);
|
|
||||||
if let Some(describe) = x.2 {
|
|
||||||
acc.2.push(describe);
|
|
||||||
}
|
|
||||||
acc
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl Default for #ty {
|
|
||||||
fn default() -> Self {
|
|
||||||
#ty::describe();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
#(#defaults)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #ty {
|
|
||||||
/// Create new instance of metrics with provided labels.
|
|
||||||
#vis fn new_with_labels(labels: impl metrics::IntoLabels + Clone) -> Self {
|
|
||||||
Self {
|
|
||||||
#(#labeled_defaults)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#describe_doc
|
|
||||||
#vis fn describe() {
|
|
||||||
#(#describes)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MetricsScope::Dynamic => {
|
|
||||||
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
|
|
||||||
.iter()
|
|
||||||
.map(|metric| {
|
|
||||||
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);
|
|
||||||
if let Some(describe) = x.2 {
|
|
||||||
acc.2.push(describe);
|
|
||||||
}
|
|
||||||
acc
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #ty {
|
|
||||||
/// Create new instance of metrics with provided scope.
|
|
||||||
#vis fn new(scope: &str) -> Self {
|
|
||||||
#ty::describe(scope);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
#(#defaults)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create new instance of metrics with provided labels.
|
|
||||||
#vis fn new_with_labels(scope: &str, labels: impl metrics::IntoLabels + Clone) -> Self {
|
|
||||||
Self {
|
|
||||||
#(#labeled_defaults)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#describe_doc
|
|
||||||
#vis fn describe(scope: &str) {
|
|
||||||
#(#describes)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(quote! {
|
|
||||||
#register_and_describe
|
|
||||||
|
|
||||||
impl std::fmt::Debug for #ty {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct(#ident_name).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct MetricsAttr {
|
|
||||||
pub(crate) scope: MetricsScope,
|
|
||||||
pub(crate) separator: Option<LitStr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MetricsAttr {
|
|
||||||
const DEFAULT_SEPARATOR: &'static str = ".";
|
|
||||||
|
|
||||||
fn separator(&self) -> String {
|
|
||||||
match &self.separator {
|
|
||||||
Some(sep) => sep.value(),
|
|
||||||
None => Self::DEFAULT_SEPARATOR.to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) enum MetricsScope {
|
|
||||||
Static(LitStr),
|
|
||||||
Dynamic,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_metrics_attr(node: &DeriveInput) -> Result<MetricsAttr> {
|
|
||||||
let metrics_attr = parse_single_required_attr(node, "metrics")?;
|
|
||||||
let parsed =
|
|
||||||
metrics_attr.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)?;
|
|
||||||
let (mut scope, mut separator, mut dynamic) = (None, None, None);
|
|
||||||
for kv in parsed {
|
|
||||||
let lit = match kv.value {
|
|
||||||
Expr::Lit(ref expr) => &expr.lit,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
if kv.path.is_ident("scope") {
|
|
||||||
if scope.is_some() {
|
|
||||||
return Err(Error::new_spanned(kv, "Duplicate `scope` value provided."))
|
|
||||||
}
|
|
||||||
let scope_lit = parse_str_lit(lit)?;
|
|
||||||
validate_metric_name(&scope_lit)?;
|
|
||||||
scope = Some(scope_lit);
|
|
||||||
} else if kv.path.is_ident("separator") {
|
|
||||||
if separator.is_some() {
|
|
||||||
return Err(Error::new_spanned(kv, "Duplicate `separator` value provided."))
|
|
||||||
}
|
|
||||||
let separator_lit = parse_str_lit(lit)?;
|
|
||||||
if !SUPPORTED_SEPARATORS.contains(&&*separator_lit.value()) {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
kv,
|
|
||||||
format!(
|
|
||||||
"Unsupported `separator` value. Supported: {}.",
|
|
||||||
SUPPORTED_SEPARATORS
|
|
||||||
.iter()
|
|
||||||
.map(|sep| format!("`{sep}`"))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
separator = Some(separator_lit);
|
|
||||||
} else if kv.path.is_ident("dynamic") {
|
|
||||||
if dynamic.is_some() {
|
|
||||||
return Err(Error::new_spanned(kv, "Duplicate `dynamic` flag provided."))
|
|
||||||
}
|
|
||||||
dynamic = Some(parse_bool_lit(lit)?.value);
|
|
||||||
} else {
|
|
||||||
return Err(Error::new_spanned(kv, "Unsupported attribute entry."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scope = match (scope, dynamic) {
|
|
||||||
(Some(scope), None | Some(false)) => MetricsScope::Static(scope),
|
|
||||||
(None, Some(true)) => MetricsScope::Dynamic,
|
|
||||||
(Some(_), Some(_)) => {
|
|
||||||
return Err(Error::new_spanned(node, "`scope = ..` conflicts with `dynamic = true`."))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
node,
|
|
||||||
"Either `scope = ..` or `dynamic = true` must be set.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(MetricsAttr { scope, separator })
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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::<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."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => 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
|
|
||||||
None => match parse_docs_to_string(field)? {
|
|
||||||
Some(docs_str) => docs_str,
|
|
||||||
None => {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
field,
|
|
||||||
"Either doc comment or `describe = ..` must be set.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
metrics.push(MetricField::Included(Metric::new(field, description, rename)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(metrics)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_metric_name(name: &LitStr) -> Result<()> {
|
|
||||||
if METRIC_NAME_RE.is_match(&name.value()) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::new_spanned(name, format!("Value must match regex {}", METRIC_NAME_RE.as_str())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_single_attr<'a, T: WithAttrs + ToTokens>(
|
|
||||||
token: &'a T,
|
|
||||||
ident: &str,
|
|
||||||
) -> Result<Option<&'a Attribute>> {
|
|
||||||
let mut attr_iter = token.attrs().iter().filter(|a| a.path().is_ident(ident));
|
|
||||||
if let Some(attr) = attr_iter.next() {
|
|
||||||
if let Some(next_attr) = attr_iter.next() {
|
|
||||||
Err(Error::new_spanned(
|
|
||||||
next_attr,
|
|
||||||
format!("Duplicate `#[{ident}(..)]` attribute provided."),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(Some(attr))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_single_required_attr<'a, T: WithAttrs + ToTokens>(
|
|
||||||
token: &'a T,
|
|
||||||
ident: &str,
|
|
||||||
) -> Result<&'a Attribute> {
|
|
||||||
if let Some(attr) = parse_single_attr(token, ident)? {
|
|
||||||
Ok(attr)
|
|
||||||
} else {
|
|
||||||
Err(Error::new_spanned(token, format!("`#[{ident}(..)]` attribute must be provided.")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_docs_to_string<T: WithAttrs>(token: &T) -> Result<Option<String>> {
|
|
||||||
let mut doc_str = None;
|
|
||||||
for attr in token.attrs() {
|
|
||||||
if let syn::Meta::NameValue(ref meta) = attr.meta {
|
|
||||||
if let Expr::Lit(ref lit) = meta.value {
|
|
||||||
if let Lit::Str(ref doc) = lit.lit {
|
|
||||||
let doc_value = doc.value().trim().to_string();
|
|
||||||
doc_str = Some(
|
|
||||||
doc_str
|
|
||||||
.map(|prev_doc_value| format!("{prev_doc_value} {doc_value}"))
|
|
||||||
.unwrap_or(doc_value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(doc_str)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_str_lit(lit: &Lit) -> Result<LitStr> {
|
|
||||||
match lit {
|
|
||||||
Lit::Str(lit_str) => Ok(lit_str.to_owned()),
|
|
||||||
_ => Err(Error::new_spanned(lit, "Value **must** be a string literal.")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_bool_lit(lit: &Lit) -> Result<LitBool> {
|
|
||||||
match lit {
|
|
||||||
Lit::Bool(lit_bool) => Ok(lit_bool.to_owned()),
|
|
||||||
_ => Err(Error::new_spanned(lit, "Value **must** be a string literal.")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,139 +0,0 @@
|
|||||||
//! This crate provides [Metrics] derive macro
|
|
||||||
|
|
||||||
#![doc(
|
|
||||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
|
||||||
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
|
|
||||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
|
||||||
)]
|
|
||||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
|
||||||
|
|
||||||
mod expand;
|
|
||||||
mod metric;
|
|
||||||
mod with_attrs;
|
|
||||||
|
|
||||||
/// The [Metrics] derive macro instruments all of the struct fields and
|
|
||||||
/// creates a [Default] implementation for the struct registering all of
|
|
||||||
/// the metrics.
|
|
||||||
///
|
|
||||||
/// Additionally, it creates a `describe` method on the struct, which
|
|
||||||
/// internally calls the describe statements for all metric fields.
|
|
||||||
///
|
|
||||||
/// Sample usage:
|
|
||||||
/// ```
|
|
||||||
/// use metrics::{Counter, Gauge, Histogram};
|
|
||||||
/// use reth_metrics_derive::Metrics;
|
|
||||||
///
|
|
||||||
/// #[derive(Metrics)]
|
|
||||||
/// #[metrics(scope = "metrics_custom")]
|
|
||||||
/// pub struct CustomMetrics {
|
|
||||||
/// /// A gauge with doc comment description.
|
|
||||||
/// gauge: Gauge,
|
|
||||||
/// #[metric(rename = "second_gauge", describe = "A gauge with metric attribute description.")]
|
|
||||||
/// gauge2: Gauge,
|
|
||||||
/// /// Some doc comment
|
|
||||||
/// #[metric(describe = "Metric attribute description will be preferred over doc comment.")]
|
|
||||||
/// counter: Counter,
|
|
||||||
/// /// A renamed histogram.
|
|
||||||
/// #[metric(rename = "histogram")]
|
|
||||||
/// histo: Histogram,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The example above will be expanded to:
|
|
||||||
/// ```
|
|
||||||
/// pub struct CustomMetrics {
|
|
||||||
/// /// A gauge with doc comment description.
|
|
||||||
/// gauge: metrics::Gauge,
|
|
||||||
/// gauge2: metrics::Gauge,
|
|
||||||
/// /// Some doc comment
|
|
||||||
/// counter: metrics::Counter,
|
|
||||||
/// /// A renamed histogram.
|
|
||||||
/// histo: metrics::Histogram,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl Default for CustomMetrics {
|
|
||||||
/// fn default() -> Self {
|
|
||||||
/// Self {
|
|
||||||
/// gauge: metrics::gauge!("metrics_custom_gauge"),
|
|
||||||
/// gauge2: metrics::gauge!("metrics_custom_second_gauge"),
|
|
||||||
/// counter: metrics::counter!("metrics_custom_counter"),
|
|
||||||
/// histo: metrics::histogram!("metrics_custom_histogram"),
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl CustomMetrics {
|
|
||||||
/// /// Describe all exposed metrics
|
|
||||||
/// pub fn describe() {
|
|
||||||
/// metrics::describe_gauge!(
|
|
||||||
/// "metrics_custom_gauge",
|
|
||||||
/// "A gauge with doc comment description."
|
|
||||||
/// );
|
|
||||||
/// metrics::describe_gauge!(
|
|
||||||
/// "metrics_custom_second_gauge",
|
|
||||||
/// "A gauge with metric attribute description."
|
|
||||||
/// );
|
|
||||||
/// metrics::describe_counter!(
|
|
||||||
/// "metrics_custom_counter",
|
|
||||||
/// "Metric attribute description will be preferred over doc comment."
|
|
||||||
/// );
|
|
||||||
/// metrics::describe_histogram!("metrics_custom_histogram", "A renamed histogram.");
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl std::fmt::Debug for CustomMetrics {
|
|
||||||
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
/// f.debug_struct("CustomMetrics").finish()
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Similarly, you can derive metrics with "dynamic" scope,
|
|
||||||
/// meaning their scope can be set at the time of instantiation.
|
|
||||||
/// For example:
|
|
||||||
/// ```
|
|
||||||
/// use reth_metrics_derive::Metrics;
|
|
||||||
///
|
|
||||||
/// #[derive(Metrics)]
|
|
||||||
/// #[metrics(dynamic = true)]
|
|
||||||
/// pub struct DynamicScopeMetrics {
|
|
||||||
/// /// A gauge with doc comment description.
|
|
||||||
/// gauge: metrics::Gauge,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The example with dynamic scope will expand to
|
|
||||||
/// ```
|
|
||||||
/// pub struct DynamicScopeMetrics {
|
|
||||||
/// /// A gauge with doc comment description.
|
|
||||||
/// gauge: metrics::Gauge,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl DynamicScopeMetrics {
|
|
||||||
/// pub fn new(scope: &str) -> Self {
|
|
||||||
/// Self { gauge: metrics::gauge!(format!("{}{}{}", scope, "_", "gauge")) }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// pub fn describe(scope: &str) {
|
|
||||||
/// metrics::describe_gauge!(
|
|
||||||
/// format!("{}{}{}", scope, "_", "gauge"),
|
|
||||||
/// "A gauge with doc comment description."
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl std::fmt::Debug for DynamicScopeMetrics {
|
|
||||||
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
/// f.debug_struct("DynamicScopeMetrics").finish()
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[proc_macro_derive(Metrics, attributes(metrics, metric))]
|
|
||||||
pub fn derive_metrics(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
expand::derive(&input).unwrap_or_else(|err| err.to_compile_error()).into()
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
use quote::quote;
|
|
||||||
use syn::{Error, Field, LitStr, Result, Type};
|
|
||||||
|
|
||||||
const COUNTER_TY: &str = "Counter";
|
|
||||||
const HISTOGRAM_TY: &str = "Histogram";
|
|
||||||
const GAUGE_TY: &str = "Gauge";
|
|
||||||
|
|
||||||
pub(crate) struct Metric<'a> {
|
|
||||||
pub(crate) field: &'a Field,
|
|
||||||
pub(crate) description: String,
|
|
||||||
rename: Option<LitStr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Metric<'a> {
|
|
||||||
pub(crate) const fn new(field: &'a Field, description: String, rename: Option<LitStr>) -> Self {
|
|
||||||
Self { field, description, rename }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn name(&self) -> String {
|
|
||||||
match self.rename.as_ref() {
|
|
||||||
Some(name) => name.value(),
|
|
||||||
None => self.field.ident.as_ref().map(ToString::to_string).unwrap_or_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn register_stmt(&self) -> Result<proc_macro2::TokenStream> {
|
|
||||||
if let Type::Path(ref path_ty) = self.field.ty {
|
|
||||||
if let Some(last) = path_ty.path.segments.last() {
|
|
||||||
let registrar = match last.ident.to_string().as_str() {
|
|
||||||
COUNTER_TY => quote! { metrics::counter! },
|
|
||||||
HISTOGRAM_TY => quote! { metrics::histogram! },
|
|
||||||
GAUGE_TY => quote! { metrics::gauge! },
|
|
||||||
_ => return Err(Error::new_spanned(path_ty, "Unsupported metric type")),
|
|
||||||
};
|
|
||||||
|
|
||||||
return Ok(quote! { #registrar })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::new_spanned(&self.field.ty, "Unsupported metric type"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn describe_stmt(&self) -> Result<proc_macro2::TokenStream> {
|
|
||||||
if let Type::Path(ref path_ty) = self.field.ty {
|
|
||||||
if let Some(last) = path_ty.path.segments.last() {
|
|
||||||
let descriptor = match last.ident.to_string().as_str() {
|
|
||||||
COUNTER_TY => quote! { metrics::describe_counter! },
|
|
||||||
HISTOGRAM_TY => quote! { metrics::describe_histogram! },
|
|
||||||
GAUGE_TY => quote! { metrics::describe_gauge! },
|
|
||||||
_ => return Err(Error::new_spanned(path_ty, "Unsupported metric type")),
|
|
||||||
};
|
|
||||||
|
|
||||||
return Ok(quote! { #descriptor })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::new_spanned(&self.field.ty, "Unsupported metric type"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
use syn::{Attribute, DeriveInput, Field};
|
|
||||||
|
|
||||||
pub(crate) trait WithAttrs {
|
|
||||||
fn attrs(&self) -> &[Attribute];
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WithAttrs for DeriveInput {
|
|
||||||
fn attrs(&self) -> &[Attribute] {
|
|
||||||
&self.attrs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WithAttrs for Field {
|
|
||||||
fn attrs(&self) -> &[Attribute] {
|
|
||||||
&self.attrs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
extern crate metrics;
|
|
||||||
extern crate reth_metrics_derive;
|
|
||||||
|
|
||||||
use metrics::Gauge;
|
|
||||||
use reth_metrics_derive::Metrics;
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics {
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics2 {
|
|
||||||
#[metric()]
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics3 {
|
|
||||||
#[metric(random = "value")]
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics4 {
|
|
||||||
#[metric(describe = 123)]
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics5 {
|
|
||||||
#[metric(rename = 123)]
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics6 {
|
|
||||||
#[metric(describe = "", describe = "")]
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics7 {
|
|
||||||
#[metric(rename = "_gauge", rename = "_gauge")]
|
|
||||||
gauge: Gauge,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope")]
|
|
||||||
struct CustomMetrics8 {
|
|
||||||
#[metric(describe = "")]
|
|
||||||
gauge: String,
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
error: Either doc comment or `describe = ..` must be set.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:12:5
|
|
||||||
|
|
|
||||||
12 | gauge: Gauge,
|
|
||||||
| ^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Either doc comment or `describe = ..` must be set.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:18:5
|
|
||||||
|
|
|
||||||
18 | / #[metric()]
|
|
||||||
19 | | gauge: Gauge,
|
|
||||||
| |________________^
|
|
||||||
|
|
||||||
error: Unsupported attribute entry.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:25:14
|
|
||||||
|
|
|
||||||
25 | #[metric(random = "value")]
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Value **must** be a string literal.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:32:25
|
|
||||||
|
|
|
||||||
32 | #[metric(describe = 123)]
|
|
||||||
| ^^^
|
|
||||||
|
|
||||||
error: Value **must** be a string literal.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:39:23
|
|
||||||
|
|
|
||||||
39 | #[metric(rename = 123)]
|
|
||||||
| ^^^
|
|
||||||
|
|
||||||
error: Duplicate `describe` value provided.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:46:29
|
|
||||||
|
|
|
||||||
46 | #[metric(describe = "", describe = "")]
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Duplicate `rename` value provided.
|
|
||||||
--> tests/compile-fail/metric_attr.rs:53:33
|
|
||||||
|
|
|
||||||
53 | #[metric(rename = "_gauge", rename = "_gauge")]
|
|
||||||
| ^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Unsupported metric type
|
|
||||||
--> tests/compile-fail/metric_attr.rs:61:12
|
|
||||||
|
|
|
||||||
61 | gauge: String,
|
|
||||||
| ^^^^^^
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
extern crate reth_metrics_derive;
|
|
||||||
use reth_metrics_derive::Metrics;
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
struct CustomMetrics;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics()]
|
|
||||||
#[metrics()]
|
|
||||||
struct CustomMetrics2;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics()]
|
|
||||||
struct CustomMetrics3;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = value)]
|
|
||||||
struct CustomMetrics4;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = 123)]
|
|
||||||
struct CustomMetrics5;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some-scope")]
|
|
||||||
struct CustomMetrics6;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "some_scope", scope = "another_scope")]
|
|
||||||
struct CustomMetrics7;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(separator = value)]
|
|
||||||
struct CustomMetrics8;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(separator = 123)]
|
|
||||||
struct CustomMetrics9;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(separator = "x")]
|
|
||||||
struct CustomMetrics10;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(separator = "_", separator = ":")]
|
|
||||||
struct CustomMetrics11;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(random = "value")]
|
|
||||||
struct CustomMetrics12;
|
|
||||||
|
|
||||||
#[derive(Metrics)]
|
|
||||||
#[metrics(scope = "scope", dynamic = true)]
|
|
||||||
struct CustomMetrics13;
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
error: `#[metrics(..)]` attribute must be provided.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:7:1
|
|
||||||
|
|
|
||||||
7 | struct CustomMetrics;
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Duplicate `#[metrics(..)]` attribute provided.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:11:1
|
|
||||||
|
|
|
||||||
11 | #[metrics()]
|
|
||||||
| ^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Either `scope = ..` or `dynamic = true` must be set.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:15:1
|
|
||||||
|
|
|
||||||
15 | / #[metrics()]
|
|
||||||
16 | | struct CustomMetrics3;
|
|
||||||
| |______________________^
|
|
||||||
|
|
||||||
error: Either `scope = ..` or `dynamic = true` must be set.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:19:1
|
|
||||||
|
|
|
||||||
19 | / #[metrics(scope = value)]
|
|
||||||
20 | | struct CustomMetrics4;
|
|
||||||
| |______________________^
|
|
||||||
|
|
||||||
error: Value **must** be a string literal.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:23:19
|
|
||||||
|
|
|
||||||
23 | #[metrics(scope = 123)]
|
|
||||||
| ^^^
|
|
||||||
|
|
||||||
error: Value must match regex ^[a-zA-Z_:.][a-zA-Z0-9_:.]*$
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:27:19
|
|
||||||
|
|
|
||||||
27 | #[metrics(scope = "some-scope")]
|
|
||||||
| ^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Duplicate `scope` value provided.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:31:33
|
|
||||||
|
|
|
||||||
31 | #[metrics(scope = "some_scope", scope = "another_scope")]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Either `scope = ..` or `dynamic = true` must be set.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:35:1
|
|
||||||
|
|
|
||||||
35 | / #[metrics(separator = value)]
|
|
||||||
36 | | struct CustomMetrics8;
|
|
||||||
| |______________________^
|
|
||||||
|
|
||||||
error: Value **must** be a string literal.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:39:23
|
|
||||||
|
|
|
||||||
39 | #[metrics(separator = 123)]
|
|
||||||
| ^^^
|
|
||||||
|
|
||||||
error: Unsupported `separator` value. Supported: `.`, `_`, `:`.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:43:11
|
|
||||||
|
|
|
||||||
43 | #[metrics(separator = "x")]
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Duplicate `separator` value provided.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:47:28
|
|
||||||
|
|
|
||||||
47 | #[metrics(separator = "_", separator = ":")]
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: Unsupported attribute entry.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:51:11
|
|
||||||
|
|
|
||||||
51 | #[metrics(random = "value")]
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: `scope = ..` conflicts with `dynamic = true`.
|
|
||||||
--> tests/compile-fail/metrics_attr.rs:55:1
|
|
||||||
|
|
|
||||||
55 | / #[metrics(scope = "scope", dynamic = true)]
|
|
||||||
56 | | struct CustomMetrics13;
|
|
||||||
| |_______________________^
|
|
||||||
@ -1,351 +0,0 @@
|
|||||||
#![allow(missing_docs)]
|
|
||||||
use metrics::{
|
|
||||||
Counter, Gauge, Histogram, Key, KeyName, Label, Metadata, Recorder, SharedString, Unit,
|
|
||||||
};
|
|
||||||
use reth_metrics_derive::Metrics;
|
|
||||||
use serial_test::serial;
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{LazyLock, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[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: LazyLock<TestRecorder> = LazyLock::new(TestRecorder::new);
|
|
||||||
|
|
||||||
fn test_describe(scope: &str) {
|
|
||||||
assert_eq!(RECORDER.metrics_len(), 4);
|
|
||||||
|
|
||||||
let gauge = RECORDER.get_metric(&format!("{scope}.gauge"));
|
|
||||||
assert!(gauge.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
gauge.unwrap(),
|
|
||||||
TestMetric {
|
|
||||||
ty: TestMetricTy::Gauge,
|
|
||||||
description: Some("A gauge with doc comment description.".to_owned()),
|
|
||||||
labels: None,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let second_gauge = RECORDER.get_metric(&format!("{scope}.second_gauge"));
|
|
||||||
assert!(second_gauge.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
second_gauge.unwrap(),
|
|
||||||
TestMetric {
|
|
||||||
ty: TestMetricTy::Gauge,
|
|
||||||
description: Some("A gauge with metric attribute description.".to_owned()),
|
|
||||||
labels: None,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let counter = RECORDER.get_metric(&format!("{scope}.counter"));
|
|
||||||
assert!(counter.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
counter.unwrap(),
|
|
||||||
TestMetric {
|
|
||||||
ty: TestMetricTy::Counter,
|
|
||||||
description: Some(
|
|
||||||
"Metric attribute description will be preferred over doc comment.".to_owned()
|
|
||||||
),
|
|
||||||
labels: None,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let histogram = RECORDER.get_metric(&format!("{scope}.histogram"));
|
|
||||||
assert!(histogram.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
histogram.unwrap(),
|
|
||||||
TestMetric {
|
|
||||||
ty: TestMetricTy::Histogram,
|
|
||||||
description: Some("A renamed histogram.".to_owned()),
|
|
||||||
labels: None,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn describe_metrics() {
|
|
||||||
let _guard = RECORDER.enter();
|
|
||||||
|
|
||||||
CustomMetrics::describe();
|
|
||||||
|
|
||||||
test_describe("metrics_custom");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn describe_dynamic_metrics() {
|
|
||||||
let _guard = RECORDER.enter();
|
|
||||||
|
|
||||||
let scope = "local_scope";
|
|
||||||
|
|
||||||
DynamicScopeMetrics::describe(scope);
|
|
||||||
|
|
||||||
test_describe(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_register(scope: &str) {
|
|
||||||
assert_eq!(RECORDER.metrics_len(), 4);
|
|
||||||
|
|
||||||
let gauge = RECORDER.get_metric(&format!("{scope}.gauge"));
|
|
||||||
assert!(gauge.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
gauge.unwrap(),
|
|
||||||
TestMetric { ty: TestMetricTy::Gauge, description: None, labels: None }
|
|
||||||
);
|
|
||||||
|
|
||||||
let second_gauge = RECORDER.get_metric(&format!("{scope}.second_gauge"));
|
|
||||||
assert!(second_gauge.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
second_gauge.unwrap(),
|
|
||||||
TestMetric { ty: TestMetricTy::Gauge, description: None, labels: None }
|
|
||||||
);
|
|
||||||
|
|
||||||
let counter = RECORDER.get_metric(&format!("{scope}.counter"));
|
|
||||||
assert!(counter.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
counter.unwrap(),
|
|
||||||
TestMetric { ty: TestMetricTy::Counter, description: None, labels: None }
|
|
||||||
);
|
|
||||||
|
|
||||||
let histogram = RECORDER.get_metric(&format!("{scope}.histogram"));
|
|
||||||
assert!(histogram.is_some());
|
|
||||||
assert_eq!(
|
|
||||||
histogram.unwrap(),
|
|
||||||
TestMetric { ty: TestMetricTy::Histogram, description: None, labels: None }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn register_metrics() {
|
|
||||||
let _guard = RECORDER.enter();
|
|
||||||
|
|
||||||
let _metrics = CustomMetrics::default();
|
|
||||||
|
|
||||||
test_register("metrics_custom");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn register_dynamic_metrics() {
|
|
||||||
let _guard = RECORDER.enter();
|
|
||||||
|
|
||||||
let scope = "local_scope";
|
|
||||||
|
|
||||||
let _metrics = DynamicScopeMetrics::new(scope);
|
|
||||||
|
|
||||||
test_register(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_labels(scope: &str) {
|
|
||||||
let test_labels = vec![Label::new("key", "value")];
|
|
||||||
|
|
||||||
let gauge = RECORDER.get_metric(&format!("{scope}.gauge"));
|
|
||||||
assert!(gauge.is_some());
|
|
||||||
let labels = gauge.unwrap().labels;
|
|
||||||
assert!(labels.is_some());
|
|
||||||
assert_eq!(labels.unwrap(), test_labels,);
|
|
||||||
|
|
||||||
let second_gauge = RECORDER.get_metric(&format!("{scope}.second_gauge"));
|
|
||||||
assert!(second_gauge.is_some());
|
|
||||||
let labels = second_gauge.unwrap().labels;
|
|
||||||
assert!(labels.is_some());
|
|
||||||
assert_eq!(labels.unwrap(), test_labels,);
|
|
||||||
|
|
||||||
let counter = RECORDER.get_metric(&format!("{scope}.counter"));
|
|
||||||
assert!(counter.is_some());
|
|
||||||
let labels = counter.unwrap().labels;
|
|
||||||
assert!(labels.is_some());
|
|
||||||
assert_eq!(labels.unwrap(), test_labels,);
|
|
||||||
|
|
||||||
let histogram = RECORDER.get_metric(&format!("{scope}.histogram"));
|
|
||||||
assert!(histogram.is_some());
|
|
||||||
let labels = histogram.unwrap().labels;
|
|
||||||
assert!(labels.is_some());
|
|
||||||
assert_eq!(labels.unwrap(), test_labels,);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn label_metrics() {
|
|
||||||
let _guard = RECORDER.enter();
|
|
||||||
|
|
||||||
let _metrics = CustomMetrics::new_with_labels(&[("key", "value")]);
|
|
||||||
|
|
||||||
test_labels("metrics_custom");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn dynamic_label_metrics() {
|
|
||||||
let _guard = RECORDER.enter();
|
|
||||||
|
|
||||||
let scope = "local_scope";
|
|
||||||
|
|
||||||
let _metrics = DynamicScopeMetrics::new_with_labels(scope, &[("key", "value")]);
|
|
||||||
|
|
||||||
test_labels(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestRecorder {
|
|
||||||
// Metrics map: key => Option<description>
|
|
||||||
metrics: Mutex<HashMap<String, TestMetric>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
|
||||||
enum TestMetricTy {
|
|
||||||
Counter,
|
|
||||||
Gauge,
|
|
||||||
Histogram,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
|
||||||
struct TestMetric {
|
|
||||||
ty: TestMetricTy,
|
|
||||||
description: Option<String>,
|
|
||||||
labels: Option<Vec<Label>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestRecorder {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self { metrics: Mutex::new(HashMap::default()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this recorder as the global recorder for the duration of the returned guard.
|
|
||||||
#[must_use]
|
|
||||||
fn enter(&'static self) -> impl Drop {
|
|
||||||
struct Reset {
|
|
||||||
recorder: &'static TestRecorder,
|
|
||||||
}
|
|
||||||
impl Drop for Reset {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.recorder.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = metrics::set_global_recorder(self);
|
|
||||||
Reset { recorder: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metrics_len(&self) -> usize {
|
|
||||||
self.metrics.lock().expect("failed to lock metrics").len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_metric(&self, key: &str) -> Option<TestMetric> {
|
|
||||||
self.metrics.lock().expect("failed to lock metrics").get(key).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_metric(
|
|
||||||
&self,
|
|
||||||
key: &str,
|
|
||||||
ty: TestMetricTy,
|
|
||||||
description: Option<String>,
|
|
||||||
labels: Option<Vec<Label>>,
|
|
||||||
) {
|
|
||||||
self.metrics
|
|
||||||
.lock()
|
|
||||||
.expect("failed to lock metrics")
|
|
||||||
.insert(key.to_owned(), TestMetric { ty, description, labels });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&self) {
|
|
||||||
self.metrics.lock().expect("failed to lock metrics").clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Recorder for &'static TestRecorder {
|
|
||||||
fn describe_counter(&self, key: KeyName, _unit: Option<Unit>, description: SharedString) {
|
|
||||||
self.record_metric(
|
|
||||||
key.as_str(),
|
|
||||||
TestMetricTy::Counter,
|
|
||||||
Some(description.into_owned()),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn describe_gauge(&self, key: KeyName, _unit: Option<Unit>, description: SharedString) {
|
|
||||||
self.record_metric(key.as_str(), TestMetricTy::Gauge, Some(description.into_owned()), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn describe_histogram(&self, key: KeyName, _unit: Option<Unit>, description: SharedString) {
|
|
||||||
self.record_metric(
|
|
||||||
key.as_str(),
|
|
||||||
TestMetricTy::Histogram,
|
|
||||||
Some(description.into_owned()),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_counter(&self, key: &Key, _metadata: &Metadata<'_>) -> Counter {
|
|
||||||
let labels_vec: Vec<Label> = key.labels().cloned().collect();
|
|
||||||
let labels = (!labels_vec.is_empty()).then_some(labels_vec);
|
|
||||||
self.record_metric(key.name(), TestMetricTy::Counter, None, labels);
|
|
||||||
Counter::noop()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_gauge(&self, key: &Key, _metadata: &Metadata<'_>) -> Gauge {
|
|
||||||
let labels_vec: Vec<Label> = key.labels().cloned().collect();
|
|
||||||
let labels = (!labels_vec.is_empty()).then_some(labels_vec);
|
|
||||||
self.record_metric(key.name(), TestMetricTy::Gauge, None, labels);
|
|
||||||
Gauge::noop()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_histogram(&self, key: &Key, _metadata: &Metadata<'_>) -> Histogram {
|
|
||||||
let labels_vec: Vec<Label> = key.labels().cloned().collect();
|
|
||||||
let labels = (!labels_vec.is_empty()).then_some(labels_vec);
|
|
||||||
self.record_metric(key.name(), TestMetricTy::Histogram, None, labels);
|
|
||||||
Histogram::noop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#![allow(missing_docs)]
|
|
||||||
#[test]
|
|
||||||
fn compile_test() {
|
|
||||||
let t = trybuild::TestCases::new();
|
|
||||||
t.compile_fail("tests/compile-fail/*.rs");
|
|
||||||
}
|
|
||||||
@ -1,9 +1,9 @@
|
|||||||
//! Support for metering senders. Facilitates debugging by exposing metrics for number of messages
|
//! Support for metering senders. Facilitates debugging by exposing metrics for number of messages
|
||||||
//! sent, number of errors, etc.
|
//! sent, number of errors, etc.
|
||||||
|
|
||||||
|
use crate::Metrics;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use metrics::Counter;
|
use metrics::Counter;
|
||||||
use reth_metrics_derive::Metrics;
|
|
||||||
use std::{
|
use std::{
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{ready, Context, Poll},
|
task::{ready, Context, Poll},
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||||
|
|
||||||
/// Metrics derive macro.
|
/// Metrics derive macro.
|
||||||
pub use reth_metrics_derive::Metrics;
|
pub use metrics_derive::Metrics;
|
||||||
|
|
||||||
/// Implementation of common metric utilities.
|
/// Implementation of common metric utilities.
|
||||||
#[cfg(feature = "common")]
|
#[cfg(feature = "common")]
|
||||||
|
|||||||
Reference in New Issue
Block a user