feat(processmonitor): Add CPU monitoring and display CPU in process list
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-08-30 - 5.6.0 - feat(processmonitor)
|
||||||
|
Add CPU monitoring and display CPU in process list
|
||||||
|
|
||||||
|
- CLI: show a CPU column in the `tspm list` output (adds formatting and placeholder name display)
|
||||||
|
- Daemon: ProcessMonitor now collects CPU usage for the process group in addition to memory
|
||||||
|
- Daemon: ProcessMonitor exposes getLastCpuUsage() and ProcessManager syncs CPU values into IProcessInfo
|
||||||
|
- Non-breaking: UI and internal stats enriched to surface CPU metrics for processes
|
||||||
|
|
||||||
## 2025-08-30 - 5.5.0 - feat(logs)
|
## 2025-08-30 - 5.5.0 - feat(logs)
|
||||||
Improve logs streaming and backlog delivery; add CLI filters and ndjson output
|
Improve logs streaming and backlog delivery; add CLI filters and ndjson output
|
||||||
|
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tspm',
|
name: '@git.zone/tspm',
|
||||||
version: '5.5.0',
|
version: '5.6.0',
|
||||||
description: 'a no fuzz process manager'
|
description: 'a no fuzz process manager'
|
||||||
}
|
}
|
||||||
|
@@ -20,13 +20,13 @@ export function registerListCommand(smartcli: plugins.smartcli.Smartcli) {
|
|||||||
|
|
||||||
console.log('Process List:');
|
console.log('Process List:');
|
||||||
console.log(
|
console.log(
|
||||||
'┌─────────┬─────────────┬───────────┬───────────┬──────────┬──────────┐',
|
'┌─────────┬─────────────┬───────────┬───────────┬──────────┬──────────┬─────────┐',
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
'│ ID │ Name │ Status │ PID │ Memory │ Restarts │',
|
'│ ID │ Name │ Status │ PID │ Memory │ CPU │ Restarts │',
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
'├─────────┼─────────────┼───────────┼───────────┼──────────┼──────────┤',
|
'├─────────┼─────────────┼───────────┼───────────┼──────────┼──────────┼──────────┤',
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const proc of processes) {
|
for (const proc of processes) {
|
||||||
@@ -38,13 +38,18 @@ export function registerListCommand(smartcli: plugins.smartcli.Smartcli) {
|
|||||||
: '\x1b[33m';
|
: '\x1b[33m';
|
||||||
const resetColor = '\x1b[0m';
|
const resetColor = '\x1b[0m';
|
||||||
|
|
||||||
|
const cpuStr = typeof proc.cpu === 'number' && isFinite(proc.cpu)
|
||||||
|
? `${proc.cpu.toFixed(1)}%`
|
||||||
|
: '-';
|
||||||
|
// Name is not part of IProcessInfo; show ID as placeholder for now
|
||||||
|
const nameDisplay = String(proc.id);
|
||||||
console.log(
|
console.log(
|
||||||
`│ ${pad(String(proc.id), 7)} │ ${pad(String(proc.id), 11)} │ ${statusColor}${pad(proc.status, 9)}${resetColor} │ ${pad((proc.pid || '-').toString(), 9)} │ ${pad(formatMemory(proc.memory), 8)} │ ${pad(proc.restarts.toString(), 8)} │`,
|
`│ ${pad(String(proc.id), 7)} │ ${pad(nameDisplay, 11)} │ ${statusColor}${pad(proc.status, 9)}${resetColor} │ ${pad((proc.pid || '-').toString(), 9)} │ ${pad(formatMemory(proc.memory), 8)} │ ${pad(cpuStr, 8)} │ ${pad(proc.restarts.toString(), 8)} │`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'└─────────┴─────────────┴───────────┴───────────┴──────────┴──────────┘',
|
'└─────────┴─────────────┴───────────┴───────────┴──────────┴──────────┴──────────┘',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ actionLabel: 'list processes' },
|
{ actionLabel: 'list processes' },
|
||||||
|
@@ -438,6 +438,13 @@ export class ProcessManager extends EventEmitter {
|
|||||||
info.uptime = uptime;
|
info.uptime = uptime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update memory and cpu from latest monitor readings
|
||||||
|
info.memory = monitor.getLastMemoryUsage();
|
||||||
|
const cpu = monitor.getLastCpuUsage();
|
||||||
|
if (Number.isFinite(cpu)) {
|
||||||
|
info.cpu = cpu;
|
||||||
|
}
|
||||||
|
|
||||||
// Update restart count
|
// Update restart count
|
||||||
info.restarts = monitor.getRestartCount();
|
info.restarts = monitor.getRestartCount();
|
||||||
|
|
||||||
|
@@ -24,6 +24,8 @@ export class ProcessMonitor extends EventEmitter {
|
|||||||
private lastRetryAt: number | null = null;
|
private lastRetryAt: number | null = null;
|
||||||
private readonly MAX_RETRIES = 10;
|
private readonly MAX_RETRIES = 10;
|
||||||
private readonly RESET_WINDOW_MS = 60 * 60 * 1000; // 1 hour
|
private readonly RESET_WINDOW_MS = 60 * 60 * 1000; // 1 hour
|
||||||
|
private lastMemoryUsage: number = 0;
|
||||||
|
private lastCpuUsage: number = 0;
|
||||||
|
|
||||||
constructor(config: IMonitorConfig & { id?: ProcessId }) {
|
constructor(config: IMonitorConfig & { id?: ProcessId }) {
|
||||||
super();
|
super();
|
||||||
@@ -260,12 +262,16 @@ export class ProcessMonitor extends EventEmitter {
|
|||||||
memoryLimit: number,
|
memoryLimit: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const memoryUsage = await this.getProcessGroupMemory(pid);
|
const { memory: memoryUsage, cpu: cpuUsage } = await this.getProcessGroupStats(pid);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Memory usage for PID ${pid}: ${this.humanReadableBytes(memoryUsage)} (${memoryUsage} bytes)`,
|
`Memory usage for PID ${pid}: ${this.humanReadableBytes(memoryUsage)} (${memoryUsage} bytes)`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Store latest readings
|
||||||
|
this.lastMemoryUsage = memoryUsage;
|
||||||
|
this.lastCpuUsage = cpuUsage;
|
||||||
|
|
||||||
// Only log memory usage in debug mode to avoid spamming
|
// Only log memory usage in debug mode to avoid spamming
|
||||||
if (process.env.TSPM_DEBUG) {
|
if (process.env.TSPM_DEBUG) {
|
||||||
this.log(
|
this.log(
|
||||||
@@ -303,7 +309,7 @@ export class ProcessMonitor extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Get the total memory usage (in bytes) for the process group (the main process and its children).
|
* Get the total memory usage (in bytes) for the process group (the main process and its children).
|
||||||
*/
|
*/
|
||||||
private getProcessGroupMemory(pid: number): Promise<number> {
|
private getProcessGroupStats(pid: number): Promise<{ memory: number; cpu: number }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Getting memory usage for process group with PID ${pid}`,
|
`Getting memory usage for process group with PID ${pid}`,
|
||||||
@@ -333,7 +339,7 @@ export class ProcessMonitor extends EventEmitter {
|
|||||||
|
|
||||||
plugins.pidusage(
|
plugins.pidusage(
|
||||||
pids,
|
pids,
|
||||||
(err: Error | null, stats: Record<string, { memory: number }>) => {
|
(err: Error | null, stats: Record<string, { memory: number; cpu: number }>) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
const processError = new ProcessError(
|
const processError = new ProcessError(
|
||||||
`Failed to get process usage stats: ${err.message}`,
|
`Failed to get process usage stats: ${err.message}`,
|
||||||
@@ -345,14 +351,16 @@ export class ProcessMonitor extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let totalMemory = 0;
|
let totalMemory = 0;
|
||||||
|
let totalCpu = 0;
|
||||||
for (const key in stats) {
|
for (const key in stats) {
|
||||||
totalMemory += stats[key].memory;
|
totalMemory += stats[key].memory;
|
||||||
|
totalCpu += Number.isFinite(stats[key].cpu) ? stats[key].cpu : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Total memory for process group: ${this.humanReadableBytes(totalMemory)}`,
|
`Total memory for process group: ${this.humanReadableBytes(totalMemory)}`,
|
||||||
);
|
);
|
||||||
resolve(totalMemory);
|
resolve({ memory: totalMemory, cpu: totalCpu });
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -448,6 +456,20 @@ export class ProcessMonitor extends EventEmitter {
|
|||||||
return this.processWrapper?.isRunning() || false;
|
return this.processWrapper?.isRunning() || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last measured memory usage for the process group (bytes)
|
||||||
|
*/
|
||||||
|
public getLastMemoryUsage(): number {
|
||||||
|
return this.lastMemoryUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last measured CPU usage for the process group (sum of group, percent)
|
||||||
|
*/
|
||||||
|
public getLastCpuUsage(): number {
|
||||||
|
return this.lastCpuUsage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method for logging messages with the instance name.
|
* Helper method for logging messages with the instance name.
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user