Skip to content

Commit

Permalink
feat(host-metrics): Add process metrics (#1449)
Browse files Browse the repository at this point in the history
* Add process metrics in @opentelemetry/host-metrics

* Lint

---------

Co-authored-by: Marc Pichler <[email protected]>
Co-authored-by: Haddas Bronfman <[email protected]>
  • Loading branch information
3 people authored Mar 31, 2023
1 parent 861b867 commit 9268716
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 5 deletions.
3 changes: 3 additions & 0 deletions packages/opentelemetry-host-metrics/src/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export enum METRIC_NAMES {
NETWORK_DROPPED = 'system.network.dropped',
NETWORK_ERRORS = 'system.network.errors',
NETWORK_IO = 'system.network.io',
PROCESS_CPU_TIME = 'process.cpu.time',
PROCESS_CPU_UTILIZATION = 'process.cpu.utilization',
PROCESS_MEMORY_USAGE = 'process.memory.usage',
}

export enum CPU_LABELS {
Expand Down
85 changes: 81 additions & 4 deletions packages/opentelemetry-host-metrics/src/metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,19 @@ import { BaseMetrics } from './BaseMetrics';
import * as api from '@opentelemetry/api';
import * as enums from './enum';

import { getCpuUsageData, getMemoryData } from './stats/common';
import {
getCpuUsageData,
getMemoryData,
getProcessCpuUsageData,
getProcessMemoryData,
} from './stats/common';
import { getNetworkData } from './stats/si';
import { CpuUsageData, MemoryData, NetworkData } from './types';
import {
CpuUsageData,
MemoryData,
NetworkData,
ProcessCpuUsageData,
} from './types';
import { throttle } from './util';

/**
Expand Down Expand Up @@ -56,7 +66,19 @@ export class HostMetrics extends BaseMetrics {
}
}

private _updateCpuUtilisation(
private _updateProcessCpuTime(
observableResult: api.ObservableResult,
processCpuUsage: ProcessCpuUsageData
): void {
observableResult.observe(processCpuUsage.user, {
state: enums.CPU_LABELS.USER,
});
observableResult.observe(processCpuUsage.system, {
state: enums.CPU_LABELS.SYSTEM,
});
}

private _updateCpuUtilization(
observableResult: api.ObservableResult,
cpuUsages: CpuUsageData[]
): void {
Expand Down Expand Up @@ -85,6 +107,18 @@ export class HostMetrics extends BaseMetrics {
}
}

private _updateProcessCpuUtilization(
observableResult: api.ObservableResult,
processCpuUsage: ProcessCpuUsageData
): void {
observableResult.observe(processCpuUsage.userP, {
state: enums.CPU_LABELS.USER,
});
observableResult.observe(processCpuUsage.systemP, {
state: enums.CPU_LABELS.SYSTEM,
});
}

private _updateMemUsage(
observableResult: api.ObservableResult,
memUsage: MemoryData
Expand All @@ -109,6 +143,13 @@ export class HostMetrics extends BaseMetrics {
});
}

private _updateProcessMemUsage(
observableResult: api.ObservableResult,
memoryUsage: number
): void {
observableResult.observe(memoryUsage);
}

private _updateNetworkDropped(
observableResult: api.ObservableResult,
networkUsages: NetworkData[]
Expand Down Expand Up @@ -179,7 +220,7 @@ export class HostMetrics extends BaseMetrics {
})
.addCallback(observableResult => {
const cpuUsageData = this._getCpuUsageData();
this._updateCpuUtilisation(observableResult, cpuUsageData);
this._updateCpuUtilization(observableResult, cpuUsageData);
});
this._meter
.createObservableGauge(enums.METRIC_NAMES.MEMORY_USAGE, {
Expand Down Expand Up @@ -221,6 +262,34 @@ export class HostMetrics extends BaseMetrics {
const networkData = await this._getNetworkData();
this._updateNetworkIO(observableResult, networkData);
});
this._meter
.createObservableCounter(enums.METRIC_NAMES.PROCESS_CPU_TIME, {
description: 'Process Cpu time in seconds',
unit: 's',
})
.addCallback(observableResult => {
const processCpuUsageData = this._getProcessCpuUsageData();
this._updateProcessCpuTime(observableResult, processCpuUsageData);
});
this._meter
.createObservableGauge(enums.METRIC_NAMES.PROCESS_CPU_UTILIZATION, {
description: 'Process Cpu usage time 0-1',
})
.addCallback(observableResult => {
const processCpuUsageData = this._getProcessCpuUsageData();
this._updateProcessCpuUtilization(
observableResult,
processCpuUsageData
);
});
this._meter
.createObservableGauge(enums.METRIC_NAMES.PROCESS_MEMORY_USAGE, {
description: 'Process Memory usage in bytes',
})
.addCallback(observableResult => {
const processMemoryData = this._getProcessMemoryData();
this._updateProcessMemUsage(observableResult, processMemoryData);
});
}

/**
Expand All @@ -236,4 +305,12 @@ export class HostMetrics extends BaseMetrics {
this._maxTimeoutUpdateMS
);
private _getNetworkData = throttle(getNetworkData, this._maxTimeoutUpdateMS);
private _getProcessCpuUsageData = throttle(
getProcessCpuUsageData,
this._maxTimeoutUpdateMS
);
private _getProcessMemoryData = throttle(
getProcessMemoryData,
this._maxTimeoutUpdateMS
);
}
33 changes: 32 additions & 1 deletion packages/opentelemetry-host-metrics/src/stats/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import * as os from 'os';

import { CpuUsageData, MemoryData } from '../types';
import { CpuUsageData, MemoryData, ProcessCpuUsageData } from '../types';

const MILLISECOND = 1 / 1e3;
let cpuUsageTime: number | undefined = undefined;
Expand Down Expand Up @@ -61,6 +61,28 @@ export function getCpuUsageData(): CpuUsageData[] {
});
}

/**
* It returns process 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 getProcessCpuUsageData(): ProcessCpuUsageData {
if (typeof cpuUsageTime !== 'number') {
cpuUsageTime = new Date().getTime() - process.uptime() * 1000;
}
const timeElapsed = (new Date().getTime() - cpuUsageTime) / 1000;
const cpuUsage: NodeJS.CpuUsage = process.cpuUsage();
const user = cpuUsage.user * MILLISECOND;
const system = cpuUsage.system * MILLISECOND;
const userP = user / timeElapsed;
const systemP = system / timeElapsed;
return {
user,
system,
userP,
systemP,
};
}

/**
* Returns memory data as absolute values
*/
Expand All @@ -80,3 +102,12 @@ export function getMemoryData(): MemoryData {
freeP: freeP, // this is frac part (0-1)
};
}

/**
* Returns process memory RSS
* The Resident Set Size, is the amount of space occupied in the main memory device (that is a subset of the total allocated memory) for the process,
* including all C++ and JavaScript objects and code.
*/
export function getProcessMemoryData(): number {
return process.memoryUsage.rss();
}
10 changes: 10 additions & 0 deletions packages/opentelemetry-host-metrics/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ export interface CpuUsageData {
niceP: number;
}

/**
* Process CPU usage data
*/
export interface ProcessCpuUsageData {
system: number;
user: number;
systemP: number;
userP: number;
}

/**
* Memory data
*/
Expand Down
29 changes: 29 additions & 0 deletions packages/opentelemetry-host-metrics/test/metric.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ describe('Host Metrics', () => {
sandbox.stub(SI, 'networkStats').callsFake(() => {
return mockedSI.networkStats();
});
sandbox.stub(process, 'cpuUsage').callsFake(() => {
return {
user: 90713560,
system: 63192630,
};
});
sandbox.stub(process.memoryUsage, 'rss').callsFake(() => {
return 123456;
});

reader = new TestMetricReader();

Expand Down Expand Up @@ -211,6 +220,26 @@ describe('Host Metrics', () => {
ensureValue(metric, { direction: 'receive', device: 'eth0' }, 123123);
ensureValue(metric, { direction: 'transmit', device: 'eth0' }, 321321);
});

it('should export Process CPU time metrics', async () => {
const metric = await getRecords(reader, 'process.cpu.time');

ensureValue(metric, { state: 'user' }, 90713.56);
ensureValue(metric, { state: 'system' }, 63192.630000000005);
});

it('should export Process CPU utilization metrics', async () => {
const metric = await getRecords(reader, 'process.cpu.utilization');

ensureValue(metric, { state: 'user' }, 30247.935978659552);
ensureValue(metric, { state: 'system' }, 21071.23374458153);
});

it('should export Process Memory usage metrics', async () => {
const metric = await getRecords(reader, 'process.memory.usage');

ensureValue(metric, {}, 123456);
});
});
});

Expand Down

0 comments on commit 9268716

Please sign in to comment.