@jsenv/os-metrics 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @jsenv/os-metrics
2
+
3
+ [![npm package](https://img.shields.io/npm/v/@jsenv/os-metrics.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/os-metrics)
4
+
5
+ A lightweight library for monitoring CPU and memory usage of both your Node.js process and the operating system.
6
+
7
+ ## Features
8
+
9
+ - Simple API for monitoring system resources
10
+ - Process-specific and OS-wide metrics
11
+ - Statistical analysis (min, max, median) for measurement periods
12
+ - Zero dependencies
13
+
14
+ ## Installation
15
+
16
+ ```console
17
+ npm install @jsenv/os-metrics
18
+ ```
19
+
20
+ ## CPU Usage Monitoring
21
+
22
+ Monitor CPU usage for both the current process and the entire operating system:
23
+
24
+ ```js
25
+ // Start monitoring
26
+ const [processCpuMonitoring, osCpuMonitoring] = startMonitoringCpuUsage();
27
+
28
+ // Take measurements at regular intervals
29
+ const interval = setInterval(() => {
30
+ const processCpuUsage = processCpuMonitoring.measure(); // Returns value between 0-1
31
+ const osCpuUsage = osCpuMonitoring.measure(); // Returns value between 0-1
32
+
33
+ console.log(`Process CPU: ${(processCpuUsage * 100).toFixed(2)}%`);
34
+ console.log(`System CPU: ${(osCpuUsage * 100).toFixed(2)}%`);
35
+ }, 500);
36
+
37
+ // When finished monitoring
38
+ clearInterval(interval);
39
+ processCpuMonitoring.end();
40
+ osCpuMonitoring.end();
41
+
42
+ // Access statistical information
43
+ const { info: processInfo } = processCpuMonitoring;
44
+ console.log({
45
+ minCpuUsage: processInfo.min,
46
+ maxCpuUsage: processInfo.max,
47
+ medianCpuUsage: processInfo.median,
48
+ });
49
+ ```
50
+
51
+ ## Measuring Usage Monitoring
52
+
53
+ Track memory consumption for both the current process and the system:
54
+
55
+ ```js
56
+ import { startMonitoringMemoryUsage } from "@jsenv/os-metrics";
57
+
58
+ // Start monitoring
59
+ const [processMemoryMonitoring, osMemoryMonitoring] =
60
+ startMonitoringMemoryUsage();
61
+
62
+ // Take measurements at regular intervals
63
+ const interval = setInterval(() => {
64
+ const processMemoryBytes = processMemoryMonitoring.measure(); // Returns bytes used
65
+ const osMemoryBytes = osMemoryMonitoring.measure(); // Returns bytes used
66
+
67
+ console.log(
68
+ `Process memory: ${(processMemoryBytes / 1024 / 1024).toFixed(2)} MB`,
69
+ );
70
+ console.log(`System memory: ${(osMemoryBytes / 1024 / 1024).toFixed(2)} MB`);
71
+ }, 500);
72
+
73
+ // When finished monitoring
74
+ clearInterval(interval);
75
+ processMemoryMonitoring.end();
76
+ osMemoryMonitoring.end();
77
+
78
+ // Access statistical information
79
+ const { info: memoryInfo } = processMemoryMonitoring;
80
+ console.log({
81
+ minMemoryUsage: `${(memoryInfo.min / 1024 / 1024).toFixed(2)} MB`,
82
+ maxMemoryUsage: `${(memoryInfo.max / 1024 / 1024).toFixed(2)} MB`,
83
+ medianMemoryUsage: `${(memoryInfo.median / 1024 / 1024).toFixed(2)} MB`,
84
+ });
85
+ ```
86
+
87
+ Each call to `measure()` returns memory usage (a number representing the number of bytes being used).
88
+ `end()` stops the monitoring and fill `.info` of the memory metric.
89
+
90
+ ```js
91
+ const { info } = processMemoryMonitoring;
92
+
93
+ info.min; // the min cpu usage between startMonitoringCpuUsage() and end()
94
+ info.max; // the max cpu usage between startMonitoringCpuUsage() and end()
95
+ info.median; // the median usage between startMonitoringCpuUsage() and end()
96
+ ```
97
+
98
+ ## API Reference
99
+
100
+ ### `startMonitoringCpuUsage()`
101
+
102
+ Starts monitoring CPU usage and returns two monitoring objects:
103
+
104
+ - First: Process CPU monitoring
105
+ - Second: OS-wide CPU monitoring
106
+
107
+ ### `startMonitoringMemoryUsage()`
108
+
109
+ Starts monitoring memory usage and returns two monitoring objects:
110
+
111
+ - First: Process memory monitoring
112
+ - Second: OS-wide memory monitoring
113
+
114
+ ### Monitoring Object Methods
115
+
116
+ Each monitoring object provides:
117
+
118
+ - `measure()`: Take a measurement and return the current value
119
+ - `end()`: Stop monitoring and calculate statistical information
120
+ - `info`: Object containing min, max, and median values (available after calling end())
121
+
122
+ ## Use Cases
123
+
124
+ - Performance testing and benchmarking
125
+ - Resource usage optimization
126
+ - System monitoring in Node.js applications
127
+ - Detecting memory leaks and CPU spikes
128
+
129
+ ## Platform Support
130
+
131
+ Works on all major operating systems supported by Node.js (Windows, macOS, Linux).
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@jsenv/os-metrics",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/jsenv/core",
8
+ "directory": "packages/independent/tooling/os-metrics"
9
+ },
10
+ "author": {
11
+ "name": "dmail",
12
+ "email": "dmaillard06@gmail.com"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "type": "module",
18
+ "main": "./src/main.js",
19
+ "files": [
20
+ "/src/"
21
+ ],
22
+ "sideEffects": false
23
+ }
@@ -0,0 +1,9 @@
1
+ import { availableParallelism, cpus } from "node:os";
2
+
3
+ export const countAvailableCpus = () => {
4
+ if (typeof availableParallelism === "function") {
5
+ return availableParallelism();
6
+ }
7
+ const cpuArray = cpus();
8
+ return cpuArray.length || 1;
9
+ };
@@ -0,0 +1,3 @@
1
+ import { totalmem } from "node:os";
2
+
3
+ export const getAvailableMemory = () => totalmem();
@@ -0,0 +1,17 @@
1
+ // https://gist.github.com/GaetanoPiazzolla/c40e1ebb9f709d091208e89baf9f4e00
2
+
3
+ import { startMeasuringTotalCpuUsage } from "./cpu_usage.js";
4
+ import { startMonitoringMetric } from "./metric_monitoring.js";
5
+
6
+ export const startMonitoringCpuUsage = () => {
7
+ const cpuUsage = startMeasuringTotalCpuUsage();
8
+ const processCpuUsageMonitoring = startMonitoringMetric(() => {
9
+ return cpuUsage.thisProcess.active;
10
+ });
11
+ const osCpuUsageMonitoring = startMonitoringMetric(() => {
12
+ return cpuUsage.overall.active;
13
+ });
14
+ const result = [processCpuUsageMonitoring, osCpuUsageMonitoring];
15
+ result.stop = cpuUsage.stop;
16
+ return result;
17
+ };
@@ -0,0 +1,219 @@
1
+ import { cpus } from "node:os";
2
+ import { cpuUsage } from "node:process";
3
+
4
+ export const startMeasuringTotalCpuUsage = () => {
5
+ let previousCpuArray = cpus();
6
+ let previousMs = Date.now();
7
+ let previousCpuUsage = cpuUsage();
8
+
9
+ const overall = {
10
+ inactive: 100,
11
+ active: 0,
12
+ system: 0,
13
+ user: 0,
14
+ };
15
+ const thisProcess = {
16
+ active: 0,
17
+ system: 0,
18
+ user: 0,
19
+ };
20
+ const details = previousCpuArray.map(() => {
21
+ return {
22
+ inactive: 100,
23
+ active: 0,
24
+ system: 0,
25
+ user: 0,
26
+ };
27
+ });
28
+
29
+ const samples = [];
30
+ const interval = setInterval(() => {
31
+ let cpuArray = cpus();
32
+ const ms = Date.now();
33
+ const ellapsedMs = ms - previousMs;
34
+ const cpuUsageSampleArray = [];
35
+ let overallSystemMs = 0;
36
+ let overallUserMs = 0;
37
+ let overallInactiveMs = 0;
38
+ let overallActiveMs = 0;
39
+ let overallMsEllapsed = 0;
40
+ let index = 0;
41
+ for (const cpu of cpuArray) {
42
+ const previousCpuTimes = previousCpuArray[index].times;
43
+ const cpuTimes = cpu.times;
44
+ const systemMs = cpuTimes.sys - previousCpuTimes.sys;
45
+ const userMs = cpuTimes.user - previousCpuTimes.user;
46
+ const activeMs = systemMs + userMs;
47
+ const inactiveMs = ellapsedMs - activeMs;
48
+ const cpuUsageSample = {
49
+ inactive: inactiveMs / ellapsedMs,
50
+ active: activeMs / ellapsedMs,
51
+ system: systemMs / ellapsedMs,
52
+ user: userMs / ellapsedMs,
53
+ };
54
+ cpuUsageSampleArray.push(cpuUsageSample);
55
+
56
+ overallSystemMs += systemMs;
57
+ overallUserMs += userMs;
58
+ overallInactiveMs += inactiveMs;
59
+ overallActiveMs += activeMs;
60
+ overallMsEllapsed += ellapsedMs;
61
+ index++;
62
+ }
63
+ const overallUsageSample = {
64
+ inactive: overallInactiveMs / overallMsEllapsed,
65
+ active: overallActiveMs / overallMsEllapsed,
66
+ system: overallSystemMs / overallMsEllapsed,
67
+ user: overallUserMs / overallMsEllapsed,
68
+ };
69
+ previousCpuArray = cpuArray;
70
+ previousMs = ms;
71
+
72
+ const processCpuUsage = cpuUsage();
73
+ const thisProcessSystemMs = Math.round(
74
+ (processCpuUsage.system - previousCpuUsage.system) / 1000,
75
+ );
76
+ const thisProcessUserMs = Math.round(
77
+ (processCpuUsage.user - previousCpuUsage.user) / 1000,
78
+ );
79
+ previousCpuUsage = processCpuUsage;
80
+
81
+ const thisProcessActiveMs = thisProcessSystemMs + thisProcessUserMs;
82
+ const thisProcessInactiveMs = overallMsEllapsed - thisProcessActiveMs;
83
+ const thisProcessSample = {
84
+ inactive: thisProcessInactiveMs / overallMsEllapsed,
85
+ active: thisProcessActiveMs / overallMsEllapsed,
86
+ system: thisProcessSystemMs / overallMsEllapsed,
87
+ user: thisProcessUserMs / overallMsEllapsed,
88
+ };
89
+ samples.push({
90
+ cpuUsageSampleArray,
91
+ overallUsageSample,
92
+ thisProcessSample,
93
+ });
94
+ if (samples.length === 10) {
95
+ per_cpu: {
96
+ let index = 0;
97
+ for (const detail of details) {
98
+ let systemSum = 0;
99
+ let userSum = 0;
100
+ let inactiveSum = 0;
101
+ let activeSum = 0;
102
+ for (const sample of samples) {
103
+ const { cpuUsageSampleArray } = sample;
104
+ const cpuUsageSample = cpuUsageSampleArray[index];
105
+ inactiveSum += cpuUsageSample.inactive;
106
+ activeSum += cpuUsageSample.active;
107
+ systemSum += cpuUsageSample.system;
108
+ userSum += cpuUsageSample.user;
109
+ }
110
+ Object.assign(detail, {
111
+ inactive: inactiveSum / samples.length,
112
+ active: activeSum / samples.length,
113
+ system: systemSum / samples.length,
114
+ user: userSum / samples.length,
115
+ });
116
+ index++;
117
+ }
118
+ }
119
+ all: {
120
+ let overallSystemSum = 0;
121
+ let overallUserSum = 0;
122
+ let overallInactiveSum = 0;
123
+ let overallActiveSum = 0;
124
+ for (const sample of samples) {
125
+ const { overallUsageSample } = sample;
126
+ overallSystemSum += overallUsageSample.system;
127
+ overallUserSum += overallUsageSample.user;
128
+ overallInactiveSum += overallUsageSample.inactive;
129
+ overallActiveSum += overallUsageSample.active;
130
+ }
131
+ Object.assign(overall, {
132
+ inactive: overallInactiveSum / samples.length,
133
+ active: overallActiveSum / samples.length,
134
+ system: overallSystemSum / samples.length,
135
+ user: overallUserSum / samples.length,
136
+ });
137
+ }
138
+ this_process: {
139
+ let thisProcessSystemSum = 0;
140
+ let thisProcessUserSum = 0;
141
+ let thisProcessInactiveSum = 0;
142
+ let thisProcessActiveSum = 0;
143
+ for (const sample of samples) {
144
+ const { thisProcessSample } = sample;
145
+ thisProcessSystemSum += thisProcessSample.system;
146
+ thisProcessUserSum += thisProcessSample.user;
147
+ thisProcessInactiveSum += thisProcessSample.inactive;
148
+ thisProcessActiveSum += thisProcessSample.active;
149
+ }
150
+ Object.assign(thisProcess, {
151
+ inactive: thisProcessInactiveSum / samples.length,
152
+ active: thisProcessActiveSum / samples.length,
153
+ system: thisProcessSystemSum / samples.length,
154
+ user: thisProcessUserSum / samples.length,
155
+ });
156
+ }
157
+ samples.length = 0;
158
+ }
159
+ }, 15);
160
+ interval.unref();
161
+
162
+ return {
163
+ overall,
164
+ thisProcess,
165
+ details,
166
+ stop: () => {
167
+ clearInterval(interval);
168
+ },
169
+ };
170
+ };
171
+
172
+ export const startMeasuringCpuUsage = () => {
173
+ let previousHrtime = process.hrtime();
174
+ let previousCpuUsage = process.cpuUsage();
175
+
176
+ const info = {
177
+ used: 0,
178
+ };
179
+
180
+ const interval = setInterval(() => {
181
+ const hrtime = process.hrtime(previousHrtime);
182
+ previousHrtime = hrtime;
183
+ const cpuUsage = process.cpuUsage(previousCpuUsage);
184
+ previousCpuUsage = cpuUsage;
185
+
186
+ const ellapsedMs = hrtime[0] * 1000 + hrtime[1] / 1000000;
187
+ const userMs = Math.round(cpuUsage.user / 1000);
188
+ const systemMs = Math.round(cpuUsage.system / 1000);
189
+ const activeMs = userMs + systemMs;
190
+ const usedRatio = activeMs / ellapsedMs;
191
+ const used = Math.round(usedRatio * 100) / 100;
192
+ info.used = used;
193
+ }, 15);
194
+ interval.unref();
195
+
196
+ return {
197
+ info,
198
+ stop: () => {
199
+ clearInterval(interval);
200
+ },
201
+ };
202
+ };
203
+
204
+ export const formatUsage = (usageInfo) => {
205
+ const { active, inactive, system, user } = usageInfo;
206
+
207
+ return `${ratioAsPercentage(active)} (system: ${ratioAsPercentage(
208
+ system,
209
+ )}, user: ${ratioAsPercentage(user)}), inactive: ${ratioAsPercentage(
210
+ inactive,
211
+ )}`;
212
+ };
213
+
214
+ export const ratioAsPercentage = (ratio) => {
215
+ const percentageAsNumber = ratio * 100;
216
+ const percentageAsNumberRounded = Math.round(percentageAsNumber);
217
+ const percentage = `${percentageAsNumberRounded}%`;
218
+ return percentage;
219
+ };
@@ -0,0 +1,17 @@
1
+ import {
2
+ formatUsage,
3
+ startMeasuringCpuUsage,
4
+ startMeasuringTotalCpuUsage,
5
+ } from "./cpu_usage.js";
6
+
7
+ const cpuUsage = startMeasuringCpuUsage();
8
+ setInterval(() => {
9
+ console.log(cpuUsage.info.used);
10
+ }, 400);
11
+
12
+ const totalCpuUsage = startMeasuringTotalCpuUsage();
13
+ setInterval(() => {
14
+ console.log(`overall: ${formatUsage(totalCpuUsage.overall)}
15
+ this process: ${formatUsage(totalCpuUsage.thisProcess)}
16
+ `);
17
+ }, 400);
package/src/main.js ADDED
@@ -0,0 +1,12 @@
1
+ export { countAvailableCpus } from "./available_cpu.js";
2
+ export { getAvailableMemory } from "./available_memory.js";
3
+ export { getOsVersion } from "./os_version.js";
4
+
5
+ export { startMonitoringCpuUsage } from "./cpu_monitoring.js";
6
+ export { startMonitoringMemoryUsage } from "./memory_monitoring.js";
7
+
8
+ export {
9
+ startMeasuringCpuUsage,
10
+ startMeasuringTotalCpuUsage,
11
+ } from "./cpu_usage.js";
12
+ export { startMonitoringMetric } from "./metric_monitoring.js";
@@ -0,0 +1,21 @@
1
+ import { freemem, totalmem } from "node:os";
2
+ import { memoryUsage } from "node:process";
3
+ import { startMonitoringMetric } from "./metric_monitoring.js";
4
+
5
+ export const startMonitoringMemoryUsage = () => {
6
+ const processMemoryUsageMonitoring = startMonitoringMetric(() => {
7
+ return memoryUsage().rss;
8
+ });
9
+ const osMemoryUsageMonitoring = startMonitoringMetric(() => {
10
+ const total = totalmem();
11
+ const free = freemem();
12
+ return total - free;
13
+ });
14
+ const stop = () => {
15
+ processMemoryUsageMonitoring.stop();
16
+ osMemoryUsageMonitoring.stop();
17
+ };
18
+ const result = [processMemoryUsageMonitoring, osMemoryUsageMonitoring];
19
+ result.stop = stop;
20
+ return result;
21
+ };
@@ -0,0 +1,44 @@
1
+ export const startMonitoringMetric = (measure) => {
2
+ const metrics = [];
3
+ const takeMeasure = () => {
4
+ const value = measure();
5
+ metrics.push(value);
6
+ return value;
7
+ };
8
+
9
+ const info = {
10
+ start: takeMeasure(),
11
+ min: null,
12
+ max: null,
13
+ median: null,
14
+ end: null,
15
+ };
16
+ return {
17
+ info,
18
+ measure: takeMeasure,
19
+ end: () => {
20
+ info.end = takeMeasure();
21
+ metrics.sort((a, b) => a - b);
22
+ info.min = metrics[0];
23
+ info.max = metrics[metrics.length - 1];
24
+ info.median = medianFromSortedArray(metrics);
25
+ metrics.length = 0;
26
+ },
27
+ };
28
+ };
29
+
30
+ export const medianFromSortedArray = (array) => {
31
+ const length = array.length;
32
+ const isOdd = length % 2 === 1;
33
+ if (isOdd) {
34
+ const medianNumberIndex = (length - 1) / 2;
35
+ const medianNumber = array[medianNumberIndex];
36
+ return medianNumber;
37
+ }
38
+ const rightMiddleNumberIndex = length / 2;
39
+ const leftMiddleNumberIndex = rightMiddleNumberIndex - 1;
40
+ const leftMiddleNumber = array[leftMiddleNumberIndex];
41
+ const rightMiddleNumber = array[rightMiddleNumberIndex];
42
+ const medianNumber = (leftMiddleNumber + rightMiddleNumber) / 2;
43
+ return medianNumber;
44
+ };
@@ -0,0 +1,3 @@
1
+ import { release } from "node:os";
2
+
3
+ export const getOsVersion = () => release();
@@ -0,0 +1,9 @@
1
+ import { memoryUsage } from "node:process";
2
+ import { startMonitoringMetric } from "./metric_monitoring.js";
3
+
4
+ export const startMeasuringProcessMemoryUsage = () => {
5
+ const processMemoryUsageMonitoring = startMonitoringMetric(() => {
6
+ return memoryUsage().rss;
7
+ });
8
+ return processMemoryUsageMonitoring;
9
+ };