mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
feat(crash): add CrashWriter for local crash dumps (cap 20, prune oldest)
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const MAX_DUMPS = 20;
|
||||||
|
|
||||||
|
class CrashWriter {
|
||||||
|
constructor(dir) {
|
||||||
|
this.dir = dir;
|
||||||
|
this._counter = 0;
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUncaught(err, kind) {
|
||||||
|
try {
|
||||||
|
const filename = `${Date.now()}-${++this._counter}-${kind}.json`;
|
||||||
|
const payload = {
|
||||||
|
kind,
|
||||||
|
message: err && err.message,
|
||||||
|
stack: err && err.stack,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
fs.writeFileSync(path.join(this.dir, filename), JSON.stringify(payload, null, 2));
|
||||||
|
this._prune();
|
||||||
|
} catch (writeErr) {
|
||||||
|
console.error('[crash-writer] dump write failed:', writeErr.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_prune() {
|
||||||
|
const files = fs.readdirSync(this.dir).sort();
|
||||||
|
while (files.length > MAX_DUMPS) {
|
||||||
|
const oldest = files.shift();
|
||||||
|
try { fs.unlinkSync(path.join(this.dir, oldest)); } catch (_) { /* ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list() {
|
||||||
|
if (!fs.existsSync(this.dir)) return [];
|
||||||
|
return fs.readdirSync(this.dir)
|
||||||
|
.filter((f) => f.endsWith('.json'))
|
||||||
|
.sort()
|
||||||
|
.reverse()
|
||||||
|
.map((filename) => {
|
||||||
|
const full = path.join(this.dir, filename);
|
||||||
|
const data = JSON.parse(fs.readFileSync(full, 'utf-8'));
|
||||||
|
return { filename, ...data };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(filename) {
|
||||||
|
const full = path.join(this.dir, filename);
|
||||||
|
if (fs.existsSync(full)) fs.unlinkSync(full);
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
return this.dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { CrashWriter };
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
const { CrashWriter } = require('../../../../src/main/updater/crash-writer');
|
||||||
|
|
||||||
|
function tmpDir() {
|
||||||
|
return fs.mkdtempSync(path.join(os.tmpdir(), 'crash-test-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('CrashWriter', () => {
|
||||||
|
test('writes a JSON dump for an uncaught exception', () => {
|
||||||
|
const dir = tmpDir();
|
||||||
|
const writer = new CrashWriter(dir);
|
||||||
|
const err = new Error('boom');
|
||||||
|
err.stack = 'Error: boom\n at test.js:1:1';
|
||||||
|
writer.handleUncaught(err, 'uncaughtException');
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
expect(files.length).toBe(1);
|
||||||
|
const data = JSON.parse(fs.readFileSync(path.join(dir, files[0]), 'utf-8'));
|
||||||
|
expect(data.kind).toBe('uncaughtException');
|
||||||
|
expect(data.message).toBe('boom');
|
||||||
|
expect(data.stack).toContain('test.js:1:1');
|
||||||
|
fs.rmSync(dir, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('caps at 20 dumps, pruning oldest', () => {
|
||||||
|
const dir = tmpDir();
|
||||||
|
const writer = new CrashWriter(dir);
|
||||||
|
for (let i = 0; i < 25; i++) {
|
||||||
|
writer.handleUncaught(new Error(`e${i}`), 'unhandledRejection');
|
||||||
|
}
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
expect(files.length).toBe(20);
|
||||||
|
fs.rmSync(dir, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns the list of dumps', () => {
|
||||||
|
const dir = tmpDir();
|
||||||
|
const writer = new CrashWriter(dir);
|
||||||
|
writer.handleUncaught(new Error('a'), 'uncaughtException');
|
||||||
|
writer.handleUncaught(new Error('b'), 'uncaughtException');
|
||||||
|
const list = writer.list();
|
||||||
|
expect(list.length).toBe(2);
|
||||||
|
fs.rmSync(dir, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deletes a dump by filename', () => {
|
||||||
|
const dir = tmpDir();
|
||||||
|
const writer = new CrashWriter(dir);
|
||||||
|
writer.handleUncaught(new Error('x'), 'uncaughtException');
|
||||||
|
const list = writer.list();
|
||||||
|
writer.delete(list[0].filename);
|
||||||
|
expect(writer.list().length).toBe(0);
|
||||||
|
fs.rmSync(dir, { recursive: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user