diff --git a/src/services/metrics-recorder.ts b/src/services/metrics-recorder.ts index 6783e242..972b09b0 100644 --- a/src/services/metrics-recorder.ts +++ b/src/services/metrics-recorder.ts @@ -10,7 +10,6 @@ import { Point, WriteApi } from '@influxdata/influxdb-client' import { BLOCK_TIMING_ERROR, CheckMethods } from '../utils/constants' import { CherryPicker } from './cherry-picker' -const os = require('os') const logger = require('../services/logger') export class MetricsRecorder { @@ -191,15 +190,21 @@ export class MetricsRecorder { // Redis timestamp for bulk logs const redisTimestamp = Math.floor(new Date().getTime() / 1000) + // Reduce multi-method calls for metrics/logging purposes + let simplifiedMethod = method + + if (method && method.split(',').length > 1) { + simplifiedMethod = 'multiple' + } + // InfluxDB const pointRelay = new Point('relay') .tag('applicationPublicKey', applicationPublicKey) .tag('nodePublicKey', serviceNode && !fallback ? 'network' : 'fallback') - .tag('method', method) + .tag('method', simplifiedMethod) .tag('result', result.toString()) .tag('blockchain', blockchainID) // 0021 .tag('blockchainSubdomain', blockchain) // eth-mainnet - .tag('host', os.hostname()) .tag('region', process.env.REGION || '') .floatField('bytes', bytes) .floatField('elapsedTime', elapsedTime.toFixed(4)) diff --git a/tests/unit/metrics-recorder.unit.ts b/tests/unit/metrics-recorder.unit.ts new file mode 100644 index 00000000..0f3f2f77 --- /dev/null +++ b/tests/unit/metrics-recorder.unit.ts @@ -0,0 +1,146 @@ +import { Pool } from 'pg' +import sinon from 'sinon' +import { InfluxDB, Point, WriteApi } from '@influxdata/influxdb-client' +import { Cache } from '../../src/services/cache' +import { CherryPicker } from '../../src/services/cherry-picker' +import { MetricsRecorder } from '../../src/services/metrics-recorder' + +// import { metricsRecorderMock } from '../mocks/metrics-recorder' + +const Redis = require('ioredis-mock') + +describe('Metrics Recorder (unit)', () => { + let cache: Cache + let pgPool: Pool + let influxClient: InfluxDB + let influxWriteApi: WriteApi + let writeInfluxSpy: sinon.SinonSpy + let cherryPicker: CherryPicker + let metricsRecorder: MetricsRecorder + + before('Initialize variables', async () => { + cache = new Cache(new Redis(0, ''), new Redis(1, '')) + pgPool = new Pool({ connectionString: 'database1' }) + cherryPicker = new CherryPicker({ redis: cache.remote, checkDebug: false }) + + // InfluxDB Mocking + influxClient = new InfluxDB({ url: 'https://url.com', token: 'token' }) + influxWriteApi = influxClient.getWriteApi('org', 'bucket') + + sinon.stub(pgPool) + }) + + beforeEach('Setting up mock metrics recorder', async () => { + writeInfluxSpy = sinon.spy(influxWriteApi, 'writePoint') + influxWriteApi.writePoint = writeInfluxSpy + + // Mocked Metrics Recorder + metricsRecorder = new MetricsRecorder({ + redis: cache.remote, + pgPool, + influxWriteAPIs: [influxWriteApi], + cherryPicker, + processUID: '1234', + }) + }) + + afterEach(() => { + sinon.restore() + }) + + it('Should reduce multi-method calls for metrics/logging purposes', async () => { + await metricsRecorder + .recordMetric({ + requestID: '', + applicationID: '', + applicationPublicKey: 'app-pub-key', + blockchain: 'eth-mainnet', + blockchainID: '0021', + serviceNode: 'node-xyz', + relayStart: [0, 1], + result: 500, + bytes: 5, + fallback: false, + method: 'eth_getBlockByNumber,eth_getBlockByNumber,eth_getBlockByNumber', + error: '', + code: String(''), + session: { + blockHeight: 0, + header: { + applicationPubKey: '', + chain: '', + sessionBlockHeight: 2, + }, + key: '', + nodes: [], + }, + origin: 'my-origin', + sticky: '', + gigastakeAppID: '', + url: '', + }) + .then(() => { + sinon.assert.calledTwice(writeInfluxSpy) + + const pointRelay = new Point('relay') + .tag('applicationPublicKey', 'app-pub-key') + .tag('nodePublicKey', 'network') + .tag('method', 'multiple') + .tag('result', '500') + .tag('blockchain', '0021') // 0021 + .tag('blockchainSubdomain', 'eth-mainnet') // eth-mainnet + .tag('region', process.env.REGION || '') + .floatField('bytes', 5) + + sinon.assert.calledWith(writeInfluxSpy.firstCall, sinon.match(pointRelay)) + }) + }) + + it('Should not reduce single method call for metrics/logging purposes', async () => { + await metricsRecorder + .recordMetric({ + requestID: '', + applicationID: '', + applicationPublicKey: 'app-pub-key', + blockchain: 'eth-mainnet', + blockchainID: '0021', + serviceNode: 'node-xyz', + relayStart: [0, 1], + result: 500, + bytes: 5, + fallback: false, + method: 'eth_getBlockByNumber', + error: '', + code: String(''), + session: { + blockHeight: 0, + header: { + applicationPubKey: '', + chain: '', + sessionBlockHeight: 2, + }, + key: '', + nodes: [], + }, + origin: 'my-origin', + sticky: '', + gigastakeAppID: '', + url: '', + }) + .then(() => { + sinon.assert.calledTwice(writeInfluxSpy) + + const pointRelay = new Point('relay') + .tag('applicationPublicKey', 'app-pub-key') + .tag('nodePublicKey', 'network') + .tag('method', 'eth_getBlockByNumber') + .tag('result', '500') + .tag('blockchain', '0021') // 0021 + .tag('blockchainSubdomain', 'eth-mainnet') // eth-mainnet + .tag('region', process.env.REGION || '') + .floatField('bytes', 5) + + sinon.assert.calledWith(writeInfluxSpy.firstCall, sinon.match(pointRelay)) + }) + }) +})