138 lines
4.7 KiB
JavaScript
138 lines
4.7 KiB
JavaScript
require('dotenv').config()
|
|
const logger = require('./logger')('metrics')
|
|
const { readFile } = require('./utils/file')
|
|
|
|
const {
|
|
MONITOR_HOME_START_BLOCK,
|
|
MONITOR_FOREIGN_START_BLOCK,
|
|
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE,
|
|
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE
|
|
} = process.env
|
|
|
|
function BridgeConf(type, validatorsBalanceEnable, alertTargetFunc, failureDirection) {
|
|
this.type = type
|
|
this.validatorsBalanceEnable = validatorsBalanceEnable
|
|
this.alertTargetFunc = alertTargetFunc
|
|
this.failureDirection = failureDirection
|
|
}
|
|
|
|
const BRIDGE_CONFS = [
|
|
new BridgeConf('home', MONITOR_HOME_VALIDATORS_BALANCE_ENABLE, 'executeAffirmations', 'homeToForeign'),
|
|
new BridgeConf('foreign', MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE, 'executeSignatures', 'foreignToHome')
|
|
]
|
|
|
|
function hasError(obj) {
|
|
return 'error' in obj
|
|
}
|
|
|
|
// Try to collect all metrics from JSON responses and then
|
|
// discard all unsuccessfully retrieved ones
|
|
async function getPrometheusMetrics(bridgeName) {
|
|
const responsePath = jsonName => `./responses/${bridgeName}/${jsonName}.json`
|
|
|
|
const metrics = {}
|
|
|
|
// Balance metrics
|
|
const balancesFile = readFile(responsePath('getBalances'))
|
|
|
|
if (!hasError(balancesFile)) {
|
|
const { home, foreign, ...commonBalances } = balancesFile
|
|
|
|
const balanceMetrics = {
|
|
// ERC_TO_ERC or ERC_TO_NATIVE mode
|
|
balances_home_value: home.totalSupply,
|
|
balances_home_txs_deposit: home.deposits,
|
|
balances_home_txs_withdrawal: home.withdrawals,
|
|
balances_foreign_value: foreign.erc20Balance,
|
|
balances_foreign_txs_deposit: foreign.deposits,
|
|
balances_foreign_txs_withdrawal: foreign.withdrawals,
|
|
|
|
// Not ARBITRARY_MESSAGE mode
|
|
balances_diff_value: commonBalances.balanceDiff,
|
|
balances_diff_deposit: commonBalances.depositsDiff,
|
|
balances_diff_withdrawal: commonBalances.withdrawalDiff,
|
|
|
|
// MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST or MONITOR_HOME_TO_FOREIGN_BLOCK_LIST is set
|
|
balances_unclaimed_txs: commonBalances.unclaimedDiff,
|
|
balances_unclaimed_value: commonBalances.unclaimedBalance,
|
|
|
|
// ARBITRARY_MESSAGE mode
|
|
txs_home_out: home.toForeign,
|
|
txs_home_in: home.fromForeign,
|
|
txs_foreign_out: foreign.toHome,
|
|
txs_foreign_in: foreign.fromHome,
|
|
txs_diff_home_out_oracles: commonBalances.fromHomeToForeignDiff,
|
|
txs_diff_home_out_users: commonBalances.fromHomeToForeignPBUDiff,
|
|
txs_diff_foreign_out: commonBalances.fromForeignToHomeDiff
|
|
}
|
|
|
|
const blockRanges = {
|
|
state_startblock_home: commonBalances.startBlockHome || MONITOR_HOME_START_BLOCK,
|
|
state_startblock_foreign: commonBalances.startBlockForeign || MONITOR_FOREIGN_START_BLOCK,
|
|
state_endblock_home: commonBalances.endBlockHome,
|
|
state_endblock_foreign: commonBalances.endBlockForeign
|
|
}
|
|
|
|
Object.assign(metrics, blockRanges, balanceMetrics)
|
|
}
|
|
|
|
// Validator metrics
|
|
const validatorsFile = readFile(responsePath('validators'))
|
|
|
|
if (!hasError(validatorsFile)) {
|
|
for (const bridge of BRIDGE_CONFS) {
|
|
const allValidators = validatorsFile[bridge.type].validators
|
|
const validatorAddressesWithBalanceCheck =
|
|
typeof bridge.validatorsBalanceEnable === 'string'
|
|
? bridge.validatorsBalanceEnable.split(' ')
|
|
: Object.keys(allValidators)
|
|
|
|
validatorAddressesWithBalanceCheck.forEach((addr, ind) => {
|
|
if (addr in allValidators) {
|
|
metrics[`validators_balances_${bridge.type}${ind}{address="${addr}"}`] = allValidators[addr].balance
|
|
} else {
|
|
logger.debug(`Nonexistent validator address ${addr}`)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Alert metrics
|
|
const alertsFile = readFile(responsePath('alerts'))
|
|
|
|
if (!hasError(alertsFile)) {
|
|
for (const bridge of BRIDGE_CONFS) {
|
|
Object.entries(alertsFile[bridge.alertTargetFunc].misbehavior).forEach(([period, val]) => {
|
|
metrics[`misbehavior_${bridge.type}_${period}`] = val
|
|
})
|
|
}
|
|
}
|
|
|
|
// Failure metrics
|
|
const failureFile = readFile(responsePath('failures'))
|
|
|
|
if (!hasError(failureFile)) {
|
|
for (const bridge of BRIDGE_CONFS) {
|
|
const dir = bridge.failureDirection
|
|
const failures = failureFile[dir]
|
|
metrics[`failures_${dir}_total`] = failures.total
|
|
Object.entries(failures.stats).forEach(([period, count]) => {
|
|
metrics[`failures_${dir}_${period}`] = count
|
|
})
|
|
}
|
|
}
|
|
|
|
// Pack metrcis into a plain text
|
|
return Object.entries(metrics).reduceRight(
|
|
// Prometheus supports `Nan` and possibly signed `Infinity`
|
|
// in case cast to `Number` fails
|
|
(acc, [key, val]) => {
|
|
if (typeof val === 'undefined') return acc
|
|
else return `${key} ${Number(val)}\n${acc}`
|
|
},
|
|
''
|
|
)
|
|
}
|
|
|
|
module.exports = getPrometheusMetrics
|