update
This commit is contained in:
@@ -17,7 +17,8 @@
|
|||||||
"@git.zone/tsbundle": "^2.8.3",
|
"@git.zone/tsbundle": "^2.8.3",
|
||||||
"@git.zone/tsrun": "^2.0.1",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@git.zone/tstest": "^3.1.8",
|
"@git.zone/tstest": "^3.1.8",
|
||||||
"@types/node": "^25.3.0"
|
"@types/node": "^25.3.0",
|
||||||
|
"@types/pidusage": "^2.0.2"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 1 chrome versions"
|
"last 1 chrome versions"
|
||||||
@@ -37,7 +38,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartlog": "^3.1.11",
|
"@push.rocks/smartlog": "^3.1.11",
|
||||||
"@types/pidusage": "^2.0.2",
|
|
||||||
"pidtree": "^0.6.0",
|
"pidtree": "^0.6.0",
|
||||||
"pidusage": "^4.0.1",
|
"pidusage": "^4.0.1",
|
||||||
"prom-client": "^15.1.3"
|
"prom-client": "^15.1.3"
|
||||||
|
|||||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -14,9 +14,6 @@ importers:
|
|||||||
'@push.rocks/smartlog':
|
'@push.rocks/smartlog':
|
||||||
specifier: ^3.1.11
|
specifier: ^3.1.11
|
||||||
version: 3.1.11
|
version: 3.1.11
|
||||||
'@types/pidusage':
|
|
||||||
specifier: ^2.0.2
|
|
||||||
version: 2.0.5
|
|
||||||
pidtree:
|
pidtree:
|
||||||
specifier: ^0.6.0
|
specifier: ^0.6.0
|
||||||
version: 0.6.0
|
version: 0.6.0
|
||||||
@@ -42,6 +39,9 @@ importers:
|
|||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^25.3.0
|
specifier: ^25.3.0
|
||||||
version: 25.3.0
|
version: 25.3.0
|
||||||
|
'@types/pidusage':
|
||||||
|
specifier: ^2.0.2
|
||||||
|
version: 2.0.5
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ SmartMetrics doesn't just monitor your main process – it automatically discove
|
|||||||
The library automatically detects available memory whether running on bare metal, in Docker containers, or with Node.js heap restrictions. It picks the most restrictive of:
|
The library automatically detects available memory whether running on bare metal, in Docker containers, or with Node.js heap restrictions. It picks the most restrictive of:
|
||||||
|
|
||||||
1. **System total memory** (`os.totalmem()`)
|
1. **System total memory** (`os.totalmem()`)
|
||||||
2. **Docker cgroup limit** (`/sys/fs/cgroup/memory/memory.limit_in_bytes`)
|
2. **Docker cgroup limit** – supports both cgroup v2 (`/sys/fs/cgroup/memory.max`) and cgroup v1 (`/sys/fs/cgroup/memory/memory.limit_in_bytes`)
|
||||||
3. **V8 heap size limit** (`v8.getHeapStatistics().heap_size_limit`)
|
3. **V8 heap size limit** (`v8.getHeapStatistics().heap_size_limit`)
|
||||||
|
|
||||||
This ensures accurate percentage calculations regardless of environment.
|
This ensures accurate percentage calculations regardless of environment.
|
||||||
@@ -101,8 +101,8 @@ Retrieves current system metrics as a structured object.
|
|||||||
```typescript
|
```typescript
|
||||||
{
|
{
|
||||||
process_cpu_seconds_total: number; // Total CPU time in seconds
|
process_cpu_seconds_total: number; // Total CPU time in seconds
|
||||||
nodejs_active_handles_total: number; // Active handles count
|
nodejs_active_handles_total: number; // Always 0 (deprecated Node.js API; real values tracked by Prometheus default collectors)
|
||||||
nodejs_active_requests_total: number; // Active requests count
|
nodejs_active_requests_total: number; // Always 0 (deprecated Node.js API; real values tracked by Prometheus default collectors)
|
||||||
nodejs_heap_size_total_bytes: number; // V8 heap size in bytes
|
nodejs_heap_size_total_bytes: number; // V8 heap size in bytes
|
||||||
cpuPercentage: number; // Aggregated CPU usage across all child processes
|
cpuPercentage: number; // Aggregated CPU usage across all child processes
|
||||||
cpuUsageText: string; // Human-readable CPU usage (e.g. "12.5 %")
|
cpuUsageText: string; // Human-readable CPU usage (e.g. "12.5 %")
|
||||||
|
|||||||
@@ -75,4 +75,4 @@ tap.test('should disable Prometheus endpoint', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.start();
|
export default tap.start();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class SmartMetrics {
|
|||||||
private prometheusServer?: plugins.http.Server;
|
private prometheusServer?: plugins.http.Server;
|
||||||
private prometheusPort?: number;
|
private prometheusPort?: number;
|
||||||
|
|
||||||
public async setup() {
|
public setup() {
|
||||||
const collectDefaultMetrics = plugins.promClient.collectDefaultMetrics;
|
const collectDefaultMetrics = plugins.promClient.collectDefaultMetrics;
|
||||||
this.registry = new plugins.promClient.Registry();
|
this.registry = new plugins.promClient.Registry();
|
||||||
collectDefaultMetrics({ register: this.registry });
|
collectDefaultMetrics({ register: this.registry });
|
||||||
@@ -50,28 +50,32 @@ export class SmartMetrics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private checkMemoryLimits() {
|
private checkMemoryLimits() {
|
||||||
let heapStats = plugins.v8.getHeapStatistics();
|
const heapStats = plugins.v8.getHeapStatistics();
|
||||||
let maxHeapSizeMB = heapStats.heap_size_limit / 1024 / 1024;
|
const maxHeapSizeMB = heapStats.heap_size_limit / 1024 / 1024;
|
||||||
let totalSystemMemoryMB = plugins.os.totalmem() / 1024 / 1024;
|
const totalSystemMemoryMB = plugins.os.totalmem() / 1024 / 1024;
|
||||||
|
|
||||||
let dockerMemoryLimitMB = totalSystemMemoryMB;
|
let dockerMemoryLimitMB = totalSystemMemoryMB;
|
||||||
|
|
||||||
|
// Try cgroup v2 first, then fall back to cgroup v1
|
||||||
try {
|
try {
|
||||||
let dockerMemoryLimitBytes = plugins.fs.readFileSync(
|
const cgroupV2 = plugins.fs.readFileSync('/sys/fs/cgroup/memory.max', 'utf8').trim();
|
||||||
'/sys/fs/cgroup/memory/memory.limit_in_bytes',
|
if (cgroupV2 !== 'max') {
|
||||||
'utf8'
|
dockerMemoryLimitMB = parseInt(cgroupV2, 10) / 1024 / 1024;
|
||||||
);
|
}
|
||||||
dockerMemoryLimitMB = parseInt(dockerMemoryLimitBytes, 10) / 1024 / 1024;
|
} catch {
|
||||||
} catch (error) {
|
try {
|
||||||
// Ignore - this will fail if not running in a Docker container
|
const cgroupV1 = plugins.fs.readFileSync(
|
||||||
|
'/sys/fs/cgroup/memory/memory.limit_in_bytes',
|
||||||
|
'utf8'
|
||||||
|
).trim();
|
||||||
|
dockerMemoryLimitMB = parseInt(cgroupV1, 10) / 1024 / 1024;
|
||||||
|
} catch {
|
||||||
|
// Not running in a container — use system memory
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the maximum memory to the lower value between the Docker limit and the total system memory
|
// Pick the most restrictive limit
|
||||||
this.maxMemoryMB = Math.min(totalSystemMemoryMB, dockerMemoryLimitMB, maxHeapSizeMB);
|
this.maxMemoryMB = Math.min(totalSystemMemoryMB, dockerMemoryLimitMB, maxHeapSizeMB);
|
||||||
|
|
||||||
// If the maximum old space size limit is greater than the maximum available memory, throw an error
|
|
||||||
if (maxHeapSizeMB > this.maxMemoryMB) {
|
|
||||||
throw new Error('Node.js process can use more memory than is available');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
@@ -104,7 +108,12 @@ export class SmartMetrics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getMetrics() {
|
public async getMetrics() {
|
||||||
const pids = await plugins.pidtree(process.pid);
|
let pids: number[] = [];
|
||||||
|
try {
|
||||||
|
pids = await plugins.pidtree(process.pid);
|
||||||
|
} catch {
|
||||||
|
// pidtree can fail if process tree cannot be read
|
||||||
|
}
|
||||||
const stats = await plugins.pidusage([process.pid, ...pids]);
|
const stats = await plugins.pidusage([process.pid, ...pids]);
|
||||||
|
|
||||||
let cpuPercentage = 0;
|
let cpuPercentage = 0;
|
||||||
@@ -126,8 +135,6 @@ export class SmartMetrics {
|
|||||||
memoryUsageBytes
|
memoryUsageBytes
|
||||||
)} / ${this.formatBytes(this.maxMemoryMB * 1024 * 1024)}`;
|
)} / ${this.formatBytes(this.maxMemoryMB * 1024 * 1024)}`;
|
||||||
|
|
||||||
console.log(`${cpuUsageText} ||| ${memoryUsageText} `);
|
|
||||||
|
|
||||||
// Update Prometheus gauges with current values
|
// Update Prometheus gauges with current values
|
||||||
if (this.cpuPercentageGauge) {
|
if (this.cpuPercentageGauge) {
|
||||||
this.cpuPercentageGauge.set(cpuPercentage);
|
this.cpuPercentageGauge.set(cpuPercentage);
|
||||||
|
|||||||
Reference in New Issue
Block a user