Metrics improvements (#574)

* add config for chain name

* Add wallets to config for prometheus middleware

* add igp contract name

* better insert

* Use our own chain names

* clean conf

* fix naming of chain

* be more generic to say native currency rather than token

* Use `Native` as token symbol
pull/586/head
Mattie Conover 2 years ago committed by GitHub
parent 4d9603c868
commit 4d47540bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      rust/abacus-base/src/metrics/core.rs
  2. 45
      rust/abacus-base/src/settings/chains.rs
  3. 10
      rust/config/test/test1_config.json
  4. 65
      rust/ethers-prometheus/src/lib.rs

@ -290,6 +290,11 @@ impl CoreMetrics {
}
}
/// Get the name of this agent, e.g. "relayer"
pub fn agent_name(&self) -> &str {
&self.agent_name
}
fn const_labels_str(&self) -> HashMap<&str, &str> {
self.const_labels
.iter()

@ -1,3 +1,4 @@
use ethers::signers::Signer;
use eyre::Report;
use serde::Deserialize;
@ -6,7 +7,7 @@ use abacus_ethereum::{
Connection, InboxBuilder, InboxValidatorManagerBuilder, InterchainGasPaymasterBuilder,
MakeableWithProvider, OutboxBuilder,
};
use ethers_prometheus::{ContractInfo, PrometheusMiddlewareConf};
use ethers_prometheus::{ChainInfo, ContractInfo, PrometheusMiddlewareConf, WalletInfo};
use crate::{
CoreMetrics, InboxValidatorManagerVariants, InboxValidatorManagers, InboxVariants, Inboxes,
@ -152,11 +153,25 @@ impl ChainSetup<OutboxAddresses> {
/// Get a clone of the metrics conf with correctly configured contract information.
pub fn metrics_conf(&self) -> PrometheusMiddlewareConf {
let mut cfg = self.metrics_conf.clone();
if cfg.chain.is_none() {
cfg.chain = Some(ChainInfo {
name: Some(self.name.clone()),
});
}
if let Ok(addr) = self.addresses.outbox.parse() {
cfg.contracts.entry(addr).or_insert_with(|| ContractInfo {
name: Some("outbox".into()),
});
}
if let Some(igp) = &self.addresses.interchain_gas_paymaster {
if let Ok(addr) = igp.parse() {
cfg.contracts.entry(addr).or_insert_with(|| ContractInfo {
name: Some("igp".into()),
});
}
}
cfg
}
}
@ -168,6 +183,7 @@ impl ChainSetup<InboxAddresses> {
signer: Option<Signers>,
metrics: &CoreMetrics,
) -> Result<Inboxes, Report> {
let metrics_conf = self.metrics_conf(metrics.agent_name(), &signer);
match &self.chain {
ChainConf::Ethereum(conf) => Ok(InboxVariants::Ethereum(
InboxBuilder {}
@ -183,7 +199,7 @@ impl ChainSetup<InboxAddresses> {
.into(),
},
signer,
Some((metrics.provider_metrics(), self.metrics_conf.clone())),
Some((metrics.provider_metrics(), metrics_conf)),
)
.await?,
)
@ -198,6 +214,7 @@ impl ChainSetup<InboxAddresses> {
metrics: &CoreMetrics,
) -> Result<InboxValidatorManagers, Report> {
let inbox_address = self.addresses.inbox.parse::<ethers::types::Address>()?;
let metrics_conf = self.metrics_conf(metrics.agent_name(), &signer);
match &self.chain {
ChainConf::Ethereum(conf) => Ok(InboxValidatorManagerVariants::Ethereum(
InboxValidatorManagerBuilder { inbox_address }
@ -213,7 +230,7 @@ impl ChainSetup<InboxAddresses> {
.into(),
},
signer,
Some((metrics.provider_metrics(), self.metrics_conf.clone())),
Some((metrics.provider_metrics(), metrics_conf)),
)
.await?,
)
@ -222,8 +239,26 @@ impl ChainSetup<InboxAddresses> {
}
/// Get a clone of the metrics conf with correctly configured contract information.
pub fn metrics_conf(&self) -> PrometheusMiddlewareConf {
pub fn metrics_conf(
&self,
agent_name: &str,
signer: &Option<Signers>,
) -> PrometheusMiddlewareConf {
let mut cfg = self.metrics_conf.clone();
if cfg.chain.is_none() {
cfg.chain = Some(ChainInfo {
name: Some(self.name.clone()),
});
}
if let Some(signer) = signer {
cfg.wallets
.entry(signer.address())
.or_insert_with(|| WalletInfo {
name: Some(agent_name.into()),
});
}
if let Ok(addr) = self.addresses.inbox.parse() {
cfg.contracts.entry(addr).or_insert_with(|| ContractInfo {
name: Some("inbox".into()),
@ -231,7 +266,7 @@ impl ChainSetup<InboxAddresses> {
}
if let Ok(addr) = self.addresses.validator_manager.parse() {
cfg.contracts.entry(addr).or_insert_with(|| ContractInfo {
name: Some("validator_manager".into()),
name: Some("ivm".into()),
});
}
cfg

@ -14,16 +14,6 @@
"addresses": {
"inbox": "0x4A679253410272dd5232B3Ff7cF5dbB88f295319",
"validatorManager": "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1"
},
"metricsConf": {
"wallets": {
"0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1": {
"name": "validator_manager"
},
"0x4A679253410272dd5232B3Ff7cF5dbB88f295319": {
"name": "inbox"
}
}
}
},
"test3": {

@ -68,6 +68,15 @@ pub struct ContractInfo {
pub name: Option<String>,
}
/// Some basic information about a chain.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "camelCase"))]
pub struct ChainInfo {
/// A human-friendly name for the chain. This should be a short string like "kovan".
pub name: Option<String>,
}
/// Expected label names for the `block_height` metric.
pub const BLOCK_HEIGHT_LABELS: &[&str] = &["chain"];
/// Help string for the metric.
@ -184,6 +193,9 @@ pub struct PrometheusMiddlewareConf {
/// Contract info for more useful metrics
#[cfg_attr(feature = "serde", serde(default))]
pub contracts: HashMap<Address, ContractInfo>,
/// Information about the chain this provider is for.
pub chain: Option<ChainInfo>,
}
assert_impl_all!(PrometheusMiddlewareConf: Send, Sync);
@ -208,7 +220,10 @@ impl<M: Middleware> Middleware for PrometheusMiddleware<M> {
let start = Instant::now();
let tx: TypedTransaction = tx.into();
let chain_name = metrics_chain_name(tx.chain_id().map(|id| id.as_u64()));
let chain = {
let data = self.conf.read().await;
chain_name(&data.chain).to_owned()
};
let addr_from: String = tx
.from()
.map(|v| v.encode_hex())
@ -223,7 +238,7 @@ impl<M: Middleware> Middleware for PrometheusMiddleware<M> {
if let Some(m) = &self.metrics.transaction_send_total {
m.with(&hashmap! {
"chain" => chain_name.as_str(),
"chain" => chain.as_str(),
"address_from" => addr_from.as_str(),
"address_to" => addr_to.as_str(),
"txn_status" => "dispatched"
@ -236,14 +251,14 @@ impl<M: Middleware> Middleware for PrometheusMiddleware<M> {
if let Some(m) = &self.metrics.transaction_send_duration_seconds {
let duration = (Instant::now() - start).as_secs_f64();
m.with(&hashmap! {
"chain" => chain_name.as_str(),
"chain" => chain.as_str(),
"address_from" => addr_from.as_str(),
})
.observe(duration);
}
if let Some(m) = &self.metrics.transaction_send_total {
m.with(&hashmap! {
"chain" => chain_name.as_str(),
"chain" => chain.as_str(),
"address_from" => addr_from.as_str(),
"address_to" => addr_to.as_str(),
"txn_status" => if result.is_ok() { "completed" } else { "failed" }
@ -264,7 +279,7 @@ impl<M: Middleware> Middleware for PrometheusMiddleware<M> {
if let Some(m) = &self.metrics.contract_call_duration_seconds {
let data = self.conf.read().await;
let chain_name = metrics_chain_name(tx.chain_id().map(|id| id.as_u64()));
let chain = chain_name(&data.chain);
let (contract_addr, contract_name) = tx
.to()
.and_then(|addr| match addr {
@ -278,7 +293,7 @@ impl<M: Middleware> Middleware for PrometheusMiddleware<M> {
.unwrap_or_else(|| ("".into(), "unknown".into()));
m.with(&hashmap! {
"chain" => chain_name.as_str(),
"chain" => chain,
"contract_name" => contract_name.as_str(),
"contract_address" => contract_addr.as_str()
})
@ -367,16 +382,15 @@ impl<M: Middleware + Send + Sync> PrometheusMiddleware<M> {
let client = self.inner.clone();
async move {
let chain_id = client.get_chainid().await.map(|id| id.as_u64()).ok();
let chain = metrics_chain_name(chain_id);
let data = data_ref.read().await;
let chain = chain_name(&data.chain);
debug!("Updating metrics for chain ({chain})");
if block_height.is_some() || gas_price_gwei.is_some() {
Self::update_block_details(&*client, &chain, block_height, gas_price_gwei).await;
Self::update_block_details(&*client, chain, block_height, gas_price_gwei).await;
}
let data = data_ref.read().await;
if let Some(wallet_balance) = wallet_balance {
Self::update_wallet_balances(client.clone(), &*data, &chain, wallet_balance).await;
Self::update_wallet_balances(client.clone(), &*data, chain, wallet_balance).await;
}
// more metrics to come...
@ -428,20 +442,22 @@ impl<M: Middleware + Send + Sync> PrometheusMiddleware<M> {
match client.get_balance(*wallet_addr, None).await {
Ok(balance) => {
// Okay, so Ether is not a token, but whatever, close enough.
// Okay, so the native type is not a token, but whatever, close enough.
// Note: This is ETH for many chains, but not all so that is why we use `N` and `Native`
// TODO: can we get away with scaling as 18 in all cases here? I am guessing not.
let balance = u256_as_scaled_f64(balance, 18);
trace!("Wallet {wallet_name} ({wallet_addr_str}) on chain {chain} balance is {balance}ETH");
trace!("Wallet {wallet_name} ({wallet_addr_str}) on chain {chain} balance is {balance} of the native currency");
wallet_balance_metric
.with(&hashmap! {
"chain" => chain,
"wallet_address" => wallet_addr_str.as_str(),
"wallet_name" => wallet_name,
"token_address" => "none",
"token_symbol" => "ETH",
"token_name" => "Ether"
"token_symbol" => "Native",
"token_name" => "Native"
}).set(balance)
},
Err(e) => warn!("Metric update failed for wallet {wallet_name} ({wallet_addr_str}) on chain {chain} balance for Ether; {e}")
Err(e) => warn!("Metric update failed for wallet {wallet_name} ({wallet_addr_str}) on chain {chain} balance for native currency; {e}")
}
for (token_addr, token) in data.tokens.iter() {
let token_addr_str: String = token_addr.encode_hex();
@ -478,17 +494,12 @@ impl<M: Middleware> Debug for PrometheusMiddleware<M> {
}
}
/// Get the metrics appropriate chain name from the chain ID.
pub fn metrics_chain_name(chain_id: Option<u64>) -> String {
if let Some(chain_id) = chain_id {
if let Ok(chain) = Chain::try_from(chain_id) {
format!("{chain}")
} else {
format!("{chain_id}")
}
} else {
"unknown".into()
}
/// Uniform way to name the chain.
fn chain_name(chain: &Option<ChainInfo>) -> &str {
chain
.as_ref()
.and_then(|c| c.name.as_deref())
.unwrap_or("unknown")
}
/// Convert a u256 scaled integer value into the corresponding f64 value.

Loading…
Cancel
Save