-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add env vars logging, rpc startup checks, new metrics
* feat: add ENV logs * feat: push open ENV vars to prom * refactor: simplify Metrics class * refactor: use the openKeys in the startup metrics * feat: add startup check RPC * fix: log level * feat: check all rpc * refactor: dirs * feat: remove RUN_STARTUP_CHECKS from 'yarn dev' * feat: pass startup-rpc-checks from mjs to Metrics class (ts source code) * feat: rpc startup checks metrics * feat: viem instead of ethers * chore: remove debug * refactor: rpc timeout 10 seconds * feat: remove stopping the app
- Loading branch information
Showing
9 changed files
with
248 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
export const openKeys = [ | ||
'SELF_ORIGIN', | ||
'ROOT_ORIGIN', | ||
'DOCS_ORIGIN', | ||
'HELP_ORIGIN', | ||
'RESEARCH_ORIGIN', | ||
|
||
'SUPPORTED_CHAINS', | ||
'DEFAULT_CHAIN', | ||
|
||
'CSP_TRUSTED_HOSTS', | ||
'CSP_REPORT_ONLY', | ||
'CSP_REPORT_URI', | ||
|
||
'ENABLE_QA_HELPERS', | ||
|
||
'REWARDS_BACKEND', | ||
|
||
'RATE_LIMIT', | ||
'RATE_LIMIT_TIME_FRAME', | ||
|
||
'ETH_API_BASE_PATH', | ||
'WQ_API_BASE_PATH', | ||
'MATOMO_URL', | ||
'WALLETCONNECT_PROJECT_ID', | ||
'REWARDS_BACKEND_BASE_PATH', | ||
]; | ||
|
||
export const secretKeys = [ | ||
'EL_RPC_URLS_1', | ||
'EL_RPC_URLS_5', | ||
'EL_RPC_URLS_17000', | ||
'EL_RPC_URLS_11155111', | ||
] | ||
|
||
|
||
export const logOpenEnvironmentVariables = () => { | ||
console.log('---------------------------------------------'); | ||
console.log('Log environment variables (without secrets):'); | ||
console.log('---------------------------------------------'); | ||
|
||
for (const key of openKeys) { | ||
if (!process.env.hasOwnProperty(key)) { | ||
console.error(`${key} - ERROR (not exist in process.env)`); | ||
continue; | ||
} | ||
|
||
console.info(`${key} = ${process.env[key]}`); | ||
} | ||
|
||
console.log('---------------------------------------------'); | ||
console.log(''); | ||
}; | ||
|
||
export const logSecretEnvironmentVariables = () => { | ||
console.log('---------------------------------------------'); | ||
console.log('Log secret environment variables:'); | ||
console.log('---------------------------------------------'); | ||
|
||
// console.log('process.env:', process.env) | ||
for (const key of secretKeys) { | ||
if (!process.env.hasOwnProperty(key)) { | ||
console.error(`Secret ${key} - ERROR (not exist in process.env)`); | ||
continue; | ||
} | ||
|
||
if (process.env[key].length > 0) { | ||
console.info(`Secret ${key} - OK (exist and not empty)`); | ||
} else { | ||
console.warn(`Secret ${key} - WARN (exist but empty)`); | ||
} | ||
} | ||
|
||
console.log('---------------------------------------------'); | ||
}; | ||
|
||
export const logEnvironmentVariables = () => { | ||
logOpenEnvironmentVariables(); | ||
logSecretEnvironmentVariables(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { createClient, http } from 'viem'; | ||
import { getChainId } from 'viem/actions' | ||
|
||
// Safely initialize a global variable | ||
let globalRPCCheckResults = globalThis.__rpcCheckResults || []; | ||
globalThis.__rpcCheckResults = globalRPCCheckResults; | ||
|
||
export const BROKEN_URL = 'BROKEN_URL'; | ||
export const RPC_TIMEOUT_SECONDS = 10_000; | ||
|
||
const pushRPCCheckResult = (domain, success) => { | ||
globalRPCCheckResults.push({ domain, success }); | ||
}; | ||
|
||
export const getRPCCheckResults = () => globalThis.__rpcCheckResults || []; | ||
|
||
const getRpcUrls = (chainId) => { | ||
const rpcUrls = process.env[`EL_RPC_URLS_${chainId}`]?.split(','); | ||
return rpcUrls?.filter((url) => url); | ||
}; | ||
|
||
export const startupCheckRPCs = async () => { | ||
console.info('[startupCheckRPCs] Starting...'); | ||
|
||
try { | ||
const defaultChain = parseInt(process.env.DEFAULT_CHAIN, 10); | ||
const rpcUrls = getRpcUrls(defaultChain); | ||
|
||
if (!rpcUrls || rpcUrls.length === 0) { | ||
throw new Error('[startupCheckRPCs] No RPC URLs found!'); | ||
} | ||
|
||
let errorCount = 0; | ||
|
||
for (const url of rpcUrls) { | ||
let domain; | ||
try { | ||
domain = new URL(url).hostname; | ||
} catch (err) { | ||
errorCount += 1; | ||
console.error('There is a broken URL.'); | ||
pushRPCCheckResult(BROKEN_URL, false); | ||
continue; | ||
} | ||
|
||
try { | ||
const client = createClient({ | ||
transport: http(url, { retryCount: 0, timeout: RPC_TIMEOUT_SECONDS }) | ||
}); | ||
|
||
const chainId = await getChainId(client); | ||
|
||
if (defaultChain === chainId) { | ||
pushRPCCheckResult(domain, true); | ||
console.info(`[startupCheckRPCs] RPC ${domain} works!`); | ||
} else { | ||
throw(`[startupCheckRPCs] RPC ${domain} does not work!`); | ||
} | ||
} catch (err) { | ||
errorCount += 1; | ||
pushRPCCheckResult(domain, false); | ||
console.error(`[startupCheckRPCs] Error with RPC ${domain}:`); | ||
console.error(String(err).replaceAll(rpcUrls, domain)); | ||
console.error(`[startupCheckRPCs] Timeout: ${RPC_TIMEOUT_SECONDS} seconds`); | ||
} | ||
} | ||
|
||
if (errorCount > 0) { | ||
console.info(`[startupCheckRPCs] Number of working RPCs: ${rpcUrls.length - errorCount}`); | ||
console.info(`[startupCheckRPCs] Number of broken RPCs: ${errorCount}`); | ||
} else { | ||
console.info('[startupCheckRPCs] All RPC works!'); | ||
} | ||
} catch (err) { | ||
console.error('[startupCheckRPCs] Error during startup check:'); | ||
console.error(err); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Counter, Registry } from 'prom-client'; | ||
import { METRICS_PREFIX, METRIC_NAMES } from 'consts/metrics'; | ||
|
||
export class StartupChecksRPCMetrics { | ||
requestCounter: Counter<'rpc_domain' | 'success'>; | ||
|
||
constructor(public registry: Registry) { | ||
this.requestCounter = this.requestsCounterInit(); | ||
} | ||
|
||
requestsCounterInit() { | ||
const requestsCounterName = | ||
METRICS_PREFIX + METRIC_NAMES.STARTUP_CHECKS_RPC; | ||
|
||
return new Counter({ | ||
name: requestsCounterName, | ||
help: 'The total number of RPC checks after the app started.', | ||
labelNames: ['rpc_domain', 'success'], | ||
registers: [this.registry], | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Gauge, type Registry } from 'prom-client'; | ||
import { collectStartupMetrics as collectBuildInfoMetrics } from '@lidofinance/api-metrics'; | ||
|
||
import buildInfoJson from 'build-info.json'; | ||
import { openKeys } from 'scripts/log-environment-variables.mjs'; | ||
import { getRPCCheckResults } from 'scripts/startup-checks/rpc.mjs'; | ||
|
||
import { config } from 'config'; | ||
import { METRICS_PREFIX } from 'consts/metrics'; | ||
|
||
import { StartupChecksRPCMetrics } from './startup-checks'; | ||
|
||
const collectStartupChecksRPCMetrics = (registry: Registry): void => { | ||
const rpcMetrics = new StartupChecksRPCMetrics(registry); | ||
|
||
getRPCCheckResults().forEach( | ||
(_check: { domain: string; success: boolean }) => { | ||
rpcMetrics.requestCounter | ||
.labels(_check.domain, _check.success.toString()) | ||
.inc(); | ||
}, | ||
); | ||
}; | ||
|
||
const collectEnvInfoMetrics = (registry: Registry): void => { | ||
const labelPairs = openKeys.map((key) => ({ | ||
name: key, | ||
value: process.env[key] ?? '', | ||
})); | ||
|
||
const envInfo = new Gauge({ | ||
name: METRICS_PREFIX + 'env_info', | ||
help: 'Environment variables of the current runtime', | ||
labelNames: labelPairs.map((pair) => pair.name), | ||
registers: [registry], | ||
}); | ||
envInfo.labels(...labelPairs.map((pair) => pair.value)).set(1); | ||
}; | ||
|
||
export const collectStartupMetrics = (registry: Registry): void => { | ||
collectEnvInfoMetrics(registry); | ||
|
||
collectBuildInfoMetrics({ | ||
prefix: METRICS_PREFIX, | ||
registry: registry, | ||
defaultChain: `${config.defaultChain}`, | ||
supportedChains: config.supportedChains.map((chain: number) => `${chain}`), | ||
version: buildInfoJson.version, | ||
commit: buildInfoJson.commit, | ||
branch: buildInfoJson.branch, | ||
}); | ||
|
||
collectStartupChecksRPCMetrics(registry); | ||
}; |