diff --git a/app/Explorer.ts b/app/Explorer.ts index 14b4e39ff..6ffd04735 100644 --- a/app/Explorer.ts +++ b/app/Explorer.ts @@ -21,6 +21,8 @@ import { authCheckMiddleware } from './middleware/auth-check'; import swaggerDocument from './swagger.json'; import { ExplorerError } from './common/ExplorerError'; import { localLoginStrategy } from './passport/local-login'; +// eslint-disable-next-line spellcheck/spell-checker +import { metricsRoutes } from './rest/metricsroutes'; /** * @@ -132,6 +134,10 @@ export class Explorer { platform.initializeListener(explorerconfig.sync); this.platforms.push(platform); + const metricsRouter = Express.Router(); + // Initializing metrics services + await metricsRoutes(metricsRouter, platform); + this.app.use('/metrics', metricsRouter); } } diff --git a/app/metrics/collect-metrics.ts b/app/metrics/collect-metrics.ts new file mode 100644 index 000000000..03e18ec0e --- /dev/null +++ b/app/metrics/collect-metrics.ts @@ -0,0 +1,63 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Proxy } from '../platform/fabric/Proxy'; +import { Platform } from '../platform/fabric/Platform'; +import { + metric_ledger_height, + ledger_blockchain_height, + ledger_transaction_count, + metric_node_up +} from './metrics'; + +export async function collectMetrics(platform: Platform) { + const proxy: Proxy = platform.getProxy(); + const networks = await proxy.networkList(); + for (const network of networks) { + const network_id = network.id; + // get all the channels info + const channelList = await proxy.getChannelsInfo(network_id); + for (const channelInfo of channelList) { + const channel_genesis = channelInfo.channel_genesis_hash; + ledger_blockchain_height + .labels({ + channel: channelInfo.channelname, + channel_genesis_hash: channelInfo.channel_genesis_hash + }) + .set(channelInfo.blocks); + ledger_transaction_count + .labels({ + channel: channelInfo.channelname, + channel_genesis_hash: channelInfo.channel_genesis_hash + }) + .inc(); + + // get the peer status and the ledger height + const peerStatus = await proxy.getPeersStatus(network_id, channel_genesis); + setLedgerHeight(peerStatus, channelInfo.channelname); + } + } +} + +async function setLedgerHeight(peerStatus: any[], channel: string) { + for (const peer of peerStatus) { + if (peer.peer_type === 'PEER' && typeof peer.ledger_height_low === 'number') { + metric_ledger_height + .labels({ + mspid: peer.mspid, + requests: peer.requests, + server_hostname: peer.server_hostname, + channel: channel + }) + .set(peer.ledger_height_low); + } + let status = 0; + if (peer.status === 'UP') { + status = 1; + } + metric_node_up + .labels({ node: peer.server_hostname, mspid: peer.mspid }) + .set(status); + } +} diff --git a/app/metrics/metrics.ts b/app/metrics/metrics.ts new file mode 100644 index 000000000..8dd672754 --- /dev/null +++ b/app/metrics/metrics.ts @@ -0,0 +1,42 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +import client from 'prom-client'; + +export const metric_ledger_height = new client.Gauge({ + name: 'metric_ledger_height', + help: 'metric_ledger_height_low: returns ledger height of the peer nodes', + labelNames: ['channel', 'mspid', 'requests', 'server_hostname'] +}); + +export const ledger_blockchain_height = new client.Gauge({ + name: 'ledger_blockchain_height', + help: 'ledger_blockchain_height: returns block height of channel', + labelNames: ['channel', 'channel_genesis_hash'] +}); + +export const ledger_transaction_count = new client.Counter({ + name: 'ledger_transaction_count', + help: 'metric_channel_txns: returns transaction count on a channel', + labelNames: ['channel', 'channel_genesis_hash'] +}); + +export const metric_node_up = new client.Gauge({ + name: 'metric_node_up', + help: 'metric_node_up: returns status of peer and orderer node', + labelNames: ['node', 'mspid'] +}); + +export const ledger_blockstorage_commit_time = new client.Histogram({ + name: 'ledger_blockstorage_commit_time', + help: + 'ledger_blockstorage_commit_time: Time taken in sec for committing block changes to state db.', + labelNames: ['channel', 'channel_genesis_hash'] +}); + +export const fabric_version = new client.Gauge({ + name: 'fabric_version', + help: 'fabric_version: show fabric versions', + labelNames: ['channel', 'channel_genesis_hash'] +}); diff --git a/app/rest/metricsroutes.ts b/app/rest/metricsroutes.ts new file mode 100644 index 000000000..fa483f73e --- /dev/null +++ b/app/rest/metricsroutes.ts @@ -0,0 +1,21 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Router } from 'express'; +import promClient from 'prom-client'; +import { helper } from '../common/helper'; +import { collectMetrics } from '../metrics/collect-metrics'; + +const logger = helper.getLogger('metricRoutes'); +export async function metricsRoutes(router: Router, platform: any) { + // scrap metrics for every 5 seconds + setInterval(() => { + collectMetrics(platform); + }, 5 * 1000); + + router.get('/', async (_, res) => { + logger.info('available metrics....'); + res.send(await promClient.register.metrics()); + }); +}