update
This commit is contained in:
@@ -17,7 +17,8 @@
|
||||
"@git.zone/tsbundle": "^2.8.3",
|
||||
"@git.zone/tsrun": "^2.0.1",
|
||||
"@git.zone/tstest": "^3.1.8",
|
||||
"@types/node": "^25.3.0"
|
||||
"@types/node": "^25.3.0",
|
||||
"@types/pidusage": "^2.0.2"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
@@ -37,7 +38,6 @@
|
||||
"dependencies": {
|
||||
"@push.rocks/smartdelay": "^3.0.5",
|
||||
"@push.rocks/smartlog": "^3.1.11",
|
||||
"@types/pidusage": "^2.0.2",
|
||||
"pidtree": "^0.6.0",
|
||||
"pidusage": "^4.0.1",
|
||||
"prom-client": "^15.1.3"
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -14,9 +14,6 @@ importers:
|
||||
'@push.rocks/smartlog':
|
||||
specifier: ^3.1.11
|
||||
version: 3.1.11
|
||||
'@types/pidusage':
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.5
|
||||
pidtree:
|
||||
specifier: ^0.6.0
|
||||
version: 0.6.0
|
||||
@@ -42,6 +39,9 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^25.3.0
|
||||
version: 25.3.0
|
||||
'@types/pidusage':
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.5
|
||||
|
||||
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:
|
||||
|
||||
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`)
|
||||
|
||||
This ensures accurate percentage calculations regardless of environment.
|
||||
@@ -101,8 +101,8 @@ Retrieves current system metrics as a structured object.
|
||||
```typescript
|
||||
{
|
||||
process_cpu_seconds_total: number; // Total CPU time in seconds
|
||||
nodejs_active_handles_total: number; // Active handles count
|
||||
nodejs_active_requests_total: number; // Active requests count
|
||||
nodejs_active_handles_total: number; // Always 0 (deprecated Node.js API; real values tracked by Prometheus default collectors)
|
||||
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
|
||||
cpuPercentage: number; // Aggregated CPU usage across all child processes
|
||||
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 prometheusPort?: number;
|
||||
|
||||
public async setup() {
|
||||
public setup() {
|
||||
const collectDefaultMetrics = plugins.promClient.collectDefaultMetrics;
|
||||
this.registry = new plugins.promClient.Registry();
|
||||
collectDefaultMetrics({ register: this.registry });
|
||||
@@ -50,28 +50,32 @@ export class SmartMetrics {
|
||||
}
|
||||
|
||||
private checkMemoryLimits() {
|
||||
let heapStats = plugins.v8.getHeapStatistics();
|
||||
let maxHeapSizeMB = heapStats.heap_size_limit / 1024 / 1024;
|
||||
let totalSystemMemoryMB = plugins.os.totalmem() / 1024 / 1024;
|
||||
const heapStats = plugins.v8.getHeapStatistics();
|
||||
const maxHeapSizeMB = heapStats.heap_size_limit / 1024 / 1024;
|
||||
const totalSystemMemoryMB = plugins.os.totalmem() / 1024 / 1024;
|
||||
|
||||
let dockerMemoryLimitMB = totalSystemMemoryMB;
|
||||
|
||||
// Try cgroup v2 first, then fall back to cgroup v1
|
||||
try {
|
||||
let dockerMemoryLimitBytes = plugins.fs.readFileSync(
|
||||
const cgroupV2 = plugins.fs.readFileSync('/sys/fs/cgroup/memory.max', 'utf8').trim();
|
||||
if (cgroupV2 !== 'max') {
|
||||
dockerMemoryLimitMB = parseInt(cgroupV2, 10) / 1024 / 1024;
|
||||
}
|
||||
} catch {
|
||||
try {
|
||||
const cgroupV1 = plugins.fs.readFileSync(
|
||||
'/sys/fs/cgroup/memory/memory.limit_in_bytes',
|
||||
'utf8'
|
||||
);
|
||||
dockerMemoryLimitMB = parseInt(dockerMemoryLimitBytes, 10) / 1024 / 1024;
|
||||
} catch (error) {
|
||||
// Ignore - this will fail if not running in a Docker container
|
||||
).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);
|
||||
|
||||
// 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() {
|
||||
@@ -104,7 +108,12 @@ export class SmartMetrics {
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
let cpuPercentage = 0;
|
||||
@@ -126,8 +135,6 @@ export class SmartMetrics {
|
||||
memoryUsageBytes
|
||||
)} / ${this.formatBytes(this.maxMemoryMB * 1024 * 1024)}`;
|
||||
|
||||
console.log(`${cpuUsageText} ||| ${memoryUsageText} `);
|
||||
|
||||
// Update Prometheus gauges with current values
|
||||
if (this.cpuPercentageGauge) {
|
||||
this.cpuPercentageGauge.set(cpuPercentage);
|
||||
|
||||
Reference in New Issue
Block a user