feat(daemon): Add crash log manager with rotation and integrate crash logging; improve IPC & process listener cleanup
This commit is contained in:
@@ -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...');
|
||||
|
||||
|
Reference in New Issue
Block a user