feat(daemon): Add crash log manager with rotation and integrate crash logging; improve IPC & process listener cleanup

This commit is contained in:
2025-09-01 10:32:51 +00:00
parent 9473924fcc
commit c9d924811d
9 changed files with 932 additions and 31 deletions

View File

@@ -23,6 +23,13 @@ export class ProcessWrapper extends EventEmitter {
private runId: string = '';
private stdoutRemainder: string = '';
private stderrRemainder: string = '';
// Store event handlers for cleanup
private exitHandler?: (code: number | null, signal: string | null) => void;
private errorHandler?: (error: Error) => void;
private stdoutDataHandler?: (data: Buffer) => void;
private stdoutEndHandler?: () => void;
private stderrDataHandler?: (data: Buffer) => void;
private stderrEndHandler?: () => void;
// Helper: send a signal to the process and all its children (best-effort)
private async killProcessTree(signal: NodeJS.Signals): Promise<void> {
@@ -84,7 +91,7 @@ export class ProcessWrapper extends EventEmitter {
this.startTime = new Date();
// Handle process exit
this.process.on('exit', (code, signal) => {
this.exitHandler = (code, signal) => {
const exitMessage = `Process exited with code ${code}, signal ${signal}`;
this.logger.info(exitMessage);
this.addSystemLog(exitMessage);
@@ -97,10 +104,11 @@ export class ProcessWrapper extends EventEmitter {
this.process = null;
this.emit('exit', code, signal);
});
};
this.process.on('exit', this.exitHandler);
// Handle errors
this.process.on('error', (error) => {
this.errorHandler = (error) => {
const processError = new ProcessError(
error.message,
'ERR_PROCESS_EXECUTION',
@@ -109,7 +117,8 @@ export class ProcessWrapper extends EventEmitter {
this.logger.error(processError);
this.addSystemLog(`Process error: ${processError.toString()}`);
this.emit('error', processError);
});
};
this.process.on('error', this.errorHandler);
// Capture stdout
if (this.process.stdout) {
@@ -118,7 +127,7 @@ export class ProcessWrapper extends EventEmitter {
`[ProcessWrapper] Setting up stdout listener for process ${this.process.pid}`,
);
}
this.process.stdout.on('data', (data) => {
this.stdoutDataHandler = (data) => {
if (process.env.TSPM_DEBUG) {
console.error(
`[ProcessWrapper] Received stdout data from PID ${this.process?.pid}: ${data
@@ -141,23 +150,25 @@ export class ProcessWrapper extends EventEmitter {
this.logger.debug(`Captured stdout: ${line}`);
this.addLog('stdout', line);
}
});
};
this.process.stdout.on('data', this.stdoutDataHandler);
// Flush remainder on stream end
this.process.stdout.on('end', () => {
this.stdoutEndHandler = () => {
if (this.stdoutRemainder) {
this.logger.debug(`Flushing stdout remainder: ${this.stdoutRemainder}`);
this.addLog('stdout', this.stdoutRemainder);
this.stdoutRemainder = '';
}
});
};
this.process.stdout.on('end', this.stdoutEndHandler);
} else {
this.logger.warn('Process stdout is null');
}
// Capture stderr
if (this.process.stderr) {
this.process.stderr.on('data', (data) => {
this.stderrDataHandler = (data) => {
// Add data to remainder buffer and split by newlines
const text = this.stderrRemainder + data.toString();
const lines = text.split('\n');
@@ -169,15 +180,17 @@ export class ProcessWrapper extends EventEmitter {
for (const line of lines) {
this.addLog('stderr', line);
}
});
};
this.process.stderr.on('data', this.stderrDataHandler);
// Flush remainder on stream end
this.process.stderr.on('end', () => {
this.stderrEndHandler = () => {
if (this.stderrRemainder) {
this.addLog('stderr', this.stderrRemainder);
this.stderrRemainder = '';
}
});
};
this.process.stderr.on('end', this.stderrEndHandler);
}
this.addSystemLog(`Process started with PID ${this.process.pid}`);
@@ -200,6 +213,46 @@ export class ProcessWrapper extends EventEmitter {
}
}
/**
* Clean up event listeners from process and streams
*/
private cleanupListeners(): void {
if (this.process) {
if (this.exitHandler) {
this.process.removeListener('exit', this.exitHandler);
}
if (this.errorHandler) {
this.process.removeListener('error', this.errorHandler);
}
if (this.process.stdout) {
if (this.stdoutDataHandler) {
this.process.stdout.removeListener('data', this.stdoutDataHandler);
}
if (this.stdoutEndHandler) {
this.process.stdout.removeListener('end', this.stdoutEndHandler);
}
}
if (this.process.stderr) {
if (this.stderrDataHandler) {
this.process.stderr.removeListener('data', this.stderrDataHandler);
}
if (this.stderrEndHandler) {
this.process.stderr.removeListener('end', this.stderrEndHandler);
}
}
}
// Clear references
this.exitHandler = undefined;
this.errorHandler = undefined;
this.stdoutDataHandler = undefined;
this.stdoutEndHandler = undefined;
this.stderrDataHandler = undefined;
this.stderrEndHandler = undefined;
}
/**
* Stop the wrapped process
*/
@@ -210,6 +263,9 @@ export class ProcessWrapper extends EventEmitter {
return;
}
// Clean up event listeners before stopping
this.cleanupListeners();
this.logger.info('Stopping process...');
this.addSystemLog('Stopping process...');