Compare commits

...

1 Commits

Author SHA1 Message Date
Claude
e60dc4b99f feat: add memory metrics timer for desktop memory tracking
Add a new memory-metrics module that uses app.getAppMetrics() to collect
and report memory usage across all Electron processes.

Features:
- Collects memory metrics every 4 hours by default
- Reports total working set and peak working set sizes
- Breaks down memory by process type (Browser, GPU, Utility, etc.)
- Configurable reporter callback for custom metric handling
- Console reporter included for development/debugging

This addresses the need to track p99 memory usage for Electron apps.

Notes: Added memory metrics timer API using app.getAppMetrics()
2026-02-25 05:26:02 +00:00
2 changed files with 127 additions and 0 deletions

View File

@@ -4,8 +4,20 @@ import { app, dialog, BrowserWindow, ipcMain } from 'electron/main';
import * as path from 'node:path';
import * as url from 'node:url';
import { startMemoryMetricsTimer, stopMemoryMetricsTimer, consoleMetricReporter } from './memory-metrics.js';
let mainWindow: BrowserWindow | null = null;
// Start memory metrics collection every 4 hours
app.whenReady().then(() => {
startMemoryMetricsTimer(consoleMetricReporter);
});
// Clean up timer on quit
app.on('will-quit', () => {
stopMemoryMetricsTimer();
});
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.quit();

View File

@@ -0,0 +1,115 @@
import { app } from 'electron/main';
const FOUR_HOURS_MS = 4 * 60 * 60 * 1000;
export interface MemoryMetricData {
timestamp: number;
totalWorkingSetSize: number;
totalPeakWorkingSetSize: number;
processes: Array<{
pid: number;
type: string;
name?: string;
workingSetSize: number;
peakWorkingSetSize: number;
}>;
}
export type MetricReporter = (data: MemoryMetricData) => void;
let memoryMetricsTimer: ReturnType<typeof setInterval> | null = null;
let metricReporter: MetricReporter | null = null;
/**
* Collects memory metrics from all Electron processes using app.getAppMetrics()
*/
export function collectMemoryMetrics(): MemoryMetricData {
const metrics = app.getAppMetrics();
let totalWorkingSetSize = 0;
let totalPeakWorkingSetSize = 0;
const processes = metrics.map(metric => {
const workingSetSize = metric.memory?.workingSetSize ?? 0;
const peakWorkingSetSize = metric.memory?.peakWorkingSetSize ?? 0;
totalWorkingSetSize += workingSetSize;
totalPeakWorkingSetSize += peakWorkingSetSize;
return {
pid: metric.pid,
type: metric.type,
name: metric.name,
workingSetSize,
peakWorkingSetSize
};
});
return {
timestamp: Date.now(),
totalWorkingSetSize,
totalPeakWorkingSetSize,
processes
};
}
/**
* Starts the memory metrics timer that reports every 4 hours
* @param reporter - Callback function to handle the collected metrics
* @param intervalMs - Optional custom interval (defaults to 4 hours)
*/
export function startMemoryMetricsTimer(
reporter: MetricReporter,
intervalMs: number = FOUR_HOURS_MS
): void {
if (memoryMetricsTimer) {
console.warn('Memory metrics timer is already running');
return;
}
metricReporter = reporter;
// Collect initial metrics immediately
const initialMetrics = collectMemoryMetrics();
metricReporter(initialMetrics);
// Set up recurring timer
memoryMetricsTimer = setInterval(() => {
if (metricReporter) {
const metrics = collectMemoryMetrics();
metricReporter(metrics);
}
}, intervalMs);
// Ensure timer doesn't prevent app from exiting
memoryMetricsTimer.unref();
}
/**
* Stops the memory metrics timer
*/
export function stopMemoryMetricsTimer(): void {
if (memoryMetricsTimer) {
clearInterval(memoryMetricsTimer);
memoryMetricsTimer = null;
metricReporter = null;
}
}
/**
* Default reporter that logs to console (for development/debugging)
*/
export function consoleMetricReporter(data: MemoryMetricData): void {
const totalMB = (data.totalWorkingSetSize / 1024).toFixed(2);
const peakMB = (data.totalPeakWorkingSetSize / 1024).toFixed(2);
console.log(`[Memory Metrics] ${new Date(data.timestamp).toISOString()}`);
console.log(` Total Working Set: ${totalMB} MB`);
console.log(` Peak Working Set: ${peakMB} MB`);
console.log(` Processes: ${data.processes.length}`);
for (const proc of data.processes) {
const procMB = (proc.workingSetSize / 1024).toFixed(2);
console.log(` - ${proc.type}${proc.name ? ` (${proc.name})` : ''} [PID ${proc.pid}]: ${procMB} MB`);
}
}