From 6644e74cdeb5af2dba3980b97145cfd88a07f5f4 Mon Sep 17 00:00:00 2001 From: Severin Neumann Date: Fri, 23 Apr 2021 14:37:54 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20"host-metrics"=20should=20collect=20syst?= =?UTF-8?q?em=20(os)=20cpu=20metrics=20to=20comply=20=E2=80=A6=20(#438)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Valentin Marchaud --- .../opentelemetry-host-metrics/src/enum.ts | 2 + .../opentelemetry-host-metrics/src/metric.ts | 116 ++++++++++++------ .../src/stats/common.ts | 44 ++++--- .../opentelemetry-host-metrics/src/types.ts | 5 + .../test/metric.test.ts | 36 ++++-- .../test/mocks/cpu.json | 26 +++- 6 files changed, 161 insertions(+), 68 deletions(-) diff --git a/packages/opentelemetry-host-metrics/src/enum.ts b/packages/opentelemetry-host-metrics/src/enum.ts index 00ea94e9e47..e4d113afb7c 100644 --- a/packages/opentelemetry-host-metrics/src/enum.ts +++ b/packages/opentelemetry-host-metrics/src/enum.ts @@ -28,6 +28,8 @@ export enum CPU_LABELS { USER = 'user', SYSTEM = 'system', IDLE = 'idle', + INTERRUPT = 'interrupt', + NICE = 'nice', } export enum NETWORK_LABELS { diff --git a/packages/opentelemetry-host-metrics/src/metric.ts b/packages/opentelemetry-host-metrics/src/metric.ts index 8c07cfca878..ac4e825d0b2 100644 --- a/packages/opentelemetry-host-metrics/src/metric.ts +++ b/packages/opentelemetry-host-metrics/src/metric.ts @@ -36,50 +36,90 @@ export class HostMetrics extends BaseMetrics { private _updateCpuTime( observerBatchResult: api.BatchObserverResult, - cpuUsage: CpuUsageData + cpuUsages: CpuUsageData[] ): void { - observerBatchResult.observe( - { - state: enums.CPU_LABELS.USER, - }, - [this._cpuTimeObserver?.observation(cpuUsage.user)] - ); - observerBatchResult.observe( - { - state: enums.CPU_LABELS.SYSTEM, - }, - [this._cpuTimeObserver?.observation(cpuUsage.system)] - ); - observerBatchResult.observe( - { - state: enums.CPU_LABELS.IDLE, - }, - [this._cpuTimeObserver?.observation(cpuUsage.idle)] - ); + for (let i = 0, j = cpuUsages.length; i < j; i++) { + const cpuUsage = cpuUsages[i]; + observerBatchResult.observe( + { + state: enums.CPU_LABELS.USER, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuTimeObserver?.observation(cpuUsage.user)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.SYSTEM, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuTimeObserver?.observation(cpuUsage.system)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.IDLE, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuTimeObserver?.observation(cpuUsage.idle)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.INTERRUPT, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuTimeObserver?.observation(cpuUsage.interrupt)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.NICE, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuTimeObserver?.observation(cpuUsage.nice)] + ); + } } private _updateCpuUtilisation( observerBatchResult: api.BatchObserverResult, - cpuUsage: CpuUsageData + cpuUsages: CpuUsageData[] ): void { - observerBatchResult.observe( - { - state: enums.CPU_LABELS.USER, - }, - [this._cpuUtilizationObserver?.observation(cpuUsage.userP)] - ); - observerBatchResult.observe( - { - state: enums.CPU_LABELS.SYSTEM, - }, - [this._cpuUtilizationObserver?.observation(cpuUsage.systemP)] - ); - observerBatchResult.observe( - { - state: enums.CPU_LABELS.IDLE, - }, - [this._cpuUtilizationObserver?.observation(cpuUsage.idleP)] - ); + for (let i = 0, j = cpuUsages.length; i < j; i++) { + const cpuUsage = cpuUsages[i]; + observerBatchResult.observe( + { + state: enums.CPU_LABELS.USER, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuUtilizationObserver?.observation(cpuUsage.userP)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.SYSTEM, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuUtilizationObserver?.observation(cpuUsage.systemP)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.IDLE, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuUtilizationObserver?.observation(cpuUsage.idleP)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.INTERRUPT, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuUtilizationObserver?.observation(cpuUsage.interruptP)] + ); + observerBatchResult.observe( + { + state: enums.CPU_LABELS.NICE, + cpu: cpuUsage.cpuNumber, + }, + [this._cpuUtilizationObserver?.observation(cpuUsage.niceP)] + ); + } } private _updateMemUsage( diff --git a/packages/opentelemetry-host-metrics/src/stats/common.ts b/packages/opentelemetry-host-metrics/src/stats/common.ts index 418f3fca283..28b73f09d27 100644 --- a/packages/opentelemetry-host-metrics/src/stats/common.ts +++ b/packages/opentelemetry-host-metrics/src/stats/common.ts @@ -18,37 +18,47 @@ import * as os from 'os'; import { CpuUsageData, MemoryData } from '../types'; -const MICROSECOND = 1 / 1e6; +const MILLISECOND = 1 / 1e3; let cpuUsageTime: number | undefined = undefined; /** * It returns cpu load delta from last time - to be used with SumObservers. * When called first time it will return 0 and then delta will be calculated */ -export function getCpuUsageData(): CpuUsageData { +export function getCpuUsageData(): CpuUsageData[] { if (typeof cpuUsageTime !== 'number') { cpuUsageTime = new Date().getTime() - process.uptime() * 1000; } const timeElapsed = (new Date().getTime() - cpuUsageTime) / 1000; - const elapsedUsage = process.cpuUsage(); - const user = elapsedUsage.user * MICROSECOND; - const system = elapsedUsage.system * MICROSECOND; - const idle = Math.max(0, timeElapsed - user - system); + return os.cpus().map((cpu, cpuNumber) => { + const idle = cpu.times.idle * MILLISECOND; + const user = cpu.times.user * MILLISECOND; + const system = cpu.times.sys * MILLISECOND; + const interrupt = cpu.times.irq * MILLISECOND; + const nice = cpu.times.nice * MILLISECOND; - const userP = user / timeElapsed; - const systemP = system / timeElapsed; - const idleP = idle / timeElapsed; + const idleP = idle / timeElapsed; + const userP = user / timeElapsed; + const systemP = system / timeElapsed; + const interruptP = interrupt / timeElapsed; + const niceP = nice / timeElapsed; - return { - user: user, - system: system, - idle: idle, - userP: userP, - systemP: systemP, - idleP: idleP, - }; + return { + cpuNumber: String(cpuNumber), + idle, + user, + system, + interrupt, + nice, + userP, + systemP, + idleP, + interruptP, + niceP, + }; + }); } /** diff --git a/packages/opentelemetry-host-metrics/src/types.ts b/packages/opentelemetry-host-metrics/src/types.ts index 78fa9f07db2..0e1aed4eb58 100644 --- a/packages/opentelemetry-host-metrics/src/types.ts +++ b/packages/opentelemetry-host-metrics/src/types.ts @@ -31,12 +31,17 @@ export interface NetworkData { * CPU usage data */ export interface CpuUsageData { + cpuNumber: string; system: number; user: number; idle: number; + nice: number; + interrupt: number; systemP: number; userP: number; idleP: number; + interruptP: number; + niceP: number; } /** diff --git a/packages/opentelemetry-host-metrics/test/metric.test.ts b/packages/opentelemetry-host-metrics/test/metric.test.ts index c471d73647d..df2fd6c0bd5 100644 --- a/packages/opentelemetry-host-metrics/test/metric.test.ts +++ b/packages/opentelemetry-host-metrics/test/metric.test.ts @@ -90,7 +90,7 @@ describe('Host Metrics', () => { return mockedOS.freemem(); }); sandbox.stub(os, 'totalmem').returns(mockedOS.totalmem()); - sandbox.stub(process, 'cpuUsage').returns(cpuJson); + sandbox.stub(os, 'cpus').returns(cpuJson); sandbox.stub(process, 'uptime').returns(0); sandbox.stub(SI, 'networkStats').callsFake(() => { return mockedSI.networkStats(); @@ -152,18 +152,36 @@ describe('Host Metrics', () => { it('should export CPU time metrics', () => { const records = getRecords(exportSpy.args[0][0], 'system.cpu.time'); - assert.strictEqual(records.length, 3); - ensureValue(records[0], { state: 'user' }, 1.899243); - ensureValue(records[1], { state: 'system' }, 0.258553); - ensureValue(records[2], { state: 'idle' }, 0.842204); + assert.strictEqual(records.length, 10); + + ensureValue(records[0], { state: 'user', cpu: '0' }, 90713.56); + ensureValue(records[1], { state: 'system', cpu: '0' }, 63192.630000000005); + ensureValue(records[2], { state: 'idle', cpu: '0' }, 374870.7); + ensureValue(records[3], { state: 'interrupt', cpu: '0' }, 0); + ensureValue(records[4], { state: 'nice', cpu: '0' }, 0); + + ensureValue(records[5], { state: 'user', cpu: '1' }, 11005.42); + ensureValue(records[6], { state: 'system', cpu: '1' }, 7678.12); + ensureValue(records[7], { state: 'idle', cpu: '1' }, 510034.8); + ensureValue(records[8], { state: 'interrupt', cpu: '1' }, 0); + ensureValue(records[9], { state: 'nice', cpu: '1' }, 0); }); it('should export CPU utilization metrics', () => { const records = getRecords(exportSpy.args[0][0], 'system.cpu.utilization'); - assert.strictEqual(records.length, 3); - ensureValue(records[0], { state: 'user' }, 0.633081); - ensureValue(records[1], { state: 'system' }, 0.08618433333333332); - ensureValue(records[2], { state: 'idle' }, 0.28073466666666663); + assert.strictEqual(records.length, 10); + + ensureValue(records[0], { state: 'user', cpu: '0' }, 30237.853333333333); + ensureValue(records[1], { state: 'system', cpu: '0' }, 21064.210000000003); + ensureValue(records[2], { state: 'idle', cpu: '0' }, 124956.90000000001); + ensureValue(records[3], { state: 'interrupt', cpu: '0' }, 0); + ensureValue(records[4], { state: 'nice', cpu: '0' }, 0); + + ensureValue(records[5], { state: 'user', cpu: '1' }, 3668.4733333333334); + ensureValue(records[6], { state: 'system', cpu: '1' }, 2559.3733333333334); + ensureValue(records[7], { state: 'idle', cpu: '1' }, 170011.6); + ensureValue(records[8], { state: 'interrupt', cpu: '1' }, 0); + ensureValue(records[9], { state: 'nice', cpu: '1' }, 0); }); it('should export Memory usage metrics', done => { diff --git a/packages/opentelemetry-host-metrics/test/mocks/cpu.json b/packages/opentelemetry-host-metrics/test/mocks/cpu.json index ee4ae3bf1fb..d9831b12a1e 100644 --- a/packages/opentelemetry-host-metrics/test/mocks/cpu.json +++ b/packages/opentelemetry-host-metrics/test/mocks/cpu.json @@ -1,4 +1,22 @@ -{ - "user": 1899243, - "system": 258553 -} \ No newline at end of file +[{ + "model": "CPU @ 2.60GHz", + "speed": 2600, + "times": { + "user": 90713560, + "nice": 0, + "sys": 63192630, + "idle": 374870700, + "irq": 0 + } +}, { + "model": "CPU @ 2.60GHz", + "speed": 2600, + "times": { + "user": 11005420, + "nice": 0, + "sys": 7678120, + "idle": 510034800, + "irq": 0 + } +} +]