diff --git a/README.md b/README.md index 729be8a..1978fcb 100644 --- a/README.md +++ b/README.md @@ -116,9 +116,17 @@ metrics: name: bitswap-elu description: Bitswap Event Loop Utilization interval: 500 - -version: 0.1.0 -buildDate: "20220307.1423" + memory: + name: bitswap-memory + description: Bitswap Memory Usage + interval: 1000 + cpu: + name: bitswap-cpu + description: Bitswap CPU Usage % + interval: 1000 + +version: 0.3.0 +buildDate: "20221216.1122" ``` ### Utils diff --git a/src/telemetry.js b/src/telemetry.js index 35cc10b..1e08459 100644 --- a/src/telemetry.js +++ b/src/telemetry.js @@ -1,8 +1,10 @@ import { performance } from 'node:perf_hooks' +import { memoryUsage, cpuUsage } from 'node:process' import { readFileSync } from 'fs' import { load as ymlLoad } from 'js-yaml' import promClient from 'prom-client' + const PERCENTILES = [0.1, 1, 10, 25, 50, 75, 90, 97.5, 99] class Telemetry { @@ -61,13 +63,33 @@ class Telemetry { })) } - if (metrics.process?.elu) { - this.collectEventLoopUtilization({ - name: metrics.process.elu.name, - description: metrics.process.elu.description, - interval: metrics.process.elu.interval, - registers: [this.allRegistry] - }) + if (metrics.process) { + if (metrics.process.elu) { + this.collectEventLoopUtilization({ + name: metrics.process.elu.name, + description: metrics.process.elu.description, + interval: metrics.process.elu.interval, + registers: [this.allRegistry] + }) + } + + if (metrics.process.memory) { + this.collectMemoryUsage({ + name: metrics.process.memory.name, + description: metrics.process.memory.description, + interval: metrics.process.memory.interval, + registers: [this.allRegistry] + }) + } + + if (metrics.process.cpu) { + this.collectCpuUsage({ + name: metrics.process.cpu.name, + description: metrics.process.cpu.description, + interval: metrics.process.cpu.interval, + registers: [this.allRegistry] + }) + } } } catch (err) { logger.error({ err }, 'error in telemetry constructor') @@ -92,6 +114,39 @@ class Telemetry { }, interval).unref() } + collectMemoryUsage ({ name, description, interval, registers }) { + const metric = new promClient.Gauge({ + name: name.replaceAll('-', '_'), + help: description, + registers + }) + this.metrics.set(name, metric) + + this.collectMemoryInterval = setInterval(() => { + metric.set(memoryUsage.rss()) + }, interval).unref() + } + + collectCpuUsage ({ name, description, interval, registers }) { + const metric = new promClient.Gauge({ + name: name.replaceAll('-', '_'), + help: description, + registers + }) + this.metrics.set(name, metric) + + let cpu1 = cpuUsage() + let timer1 = process.hrtime.bigint() + this.collectCpuInterval = setInterval(() => { + const cpu2 = cpuUsage(cpu1) + const timer2 = process.hrtime.bigint() + const v = 100 * cpu2.user / Number((timer2 - timer1) / 1000n) + metric.set(v) + cpu1 = cpu2 + timer1 = timer2 + }, interval).unref() + } + resetAll () { this.allRegistry.resetMetrics() } diff --git a/test/fixtures/process-metrics.yml b/test/fixtures/process-metrics.yml index 9c24bdc..efb9a2e 100644 --- a/test/fixtures/process-metrics.yml +++ b/test/fixtures/process-metrics.yml @@ -10,6 +10,14 @@ metrics: name: bitswap-elu description: Bitswap Event Loop Utilization interval: 100 + memory: + name: bitswap-memory + description: Bitswap Memory Usage + interval: 100 + cpu: + name: bitswap-cpu + description: Bitswap CPU Usage % + interval: 100 -version: 0.2.0 -buildDate: "20220307.1423" +version: 0.3.0 +buildDate: "20221215.1122" diff --git a/test/telemetry.test.js b/test/telemetry.test.js index 907b5f2..1dcf7ee 100644 --- a/test/telemetry.test.js +++ b/test/telemetry.test.js @@ -305,6 +305,28 @@ t.test('Telemetry', async t => { t.ok(telemetry.collectEluInterval) }) + t.test('should collect memory metric when set from config file', async t => { + const telemetry = new Telemetry({ configFile: processMetricConfigFile, logger }) + + const result = await telemetry.export() + + await setTimeoutAsync(500) + + t.ok(result.includes('bitswap_memory')) + t.ok(telemetry.collectMemoryInterval) + }) + + t.test('should collect cpu metric when set from config file', async t => { + const telemetry = new Telemetry({ configFile: processMetricConfigFile, logger }) + + const result = await telemetry.export() + + await setTimeoutAsync(500) + + t.ok(result.includes('bitswap_cpu')) + t.ok(telemetry.collectCpuInterval) + }) + t.test('should get a gauge metric value', async t => { const telemetry = new Telemetry({ configFile: defaultConfigFile, logger })