diff --git a/package-lock.json b/package-lock.json index bdc61c4ae1..f0a1387ce1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38812,11 +38812,11 @@ "optional": true }, "node_modules/rotating-file-stream": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-2.1.6.tgz", - "integrity": "sha512-qS0ndAlDu80MMXeRonqGMXslF0FErzcUSbcXhus3asRG4cvCS79hc5f7s0x4bPAsH6wAwyHVIeARg69VUe3JmQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-3.2.1.tgz", + "integrity": "sha512-n2B18CJb+n2VA5Tdle+1NP2toEcRv68CjAOBjHmwcyswNwMVsrN3gVRZ9ymH3sapaiGY8jc9OhhV5b6I5rAeiA==", "engines": { - "node": ">=10.0" + "node": ">=14.0" }, "funding": { "url": "https://www.blockchain.com/btc/address/12p1p5q7sK75tPyuesZmssiMYr4TKzpSCN" @@ -46433,7 +46433,7 @@ "music-metadata": "^7.12.3", "node-fetch": "^2.6.0", "reflect-metadata": "^0.1.13", - "rotating-file-stream": "^2.0.2", + "rotating-file-stream": "^3.2.1", "sqlite3": "^5.1.2", "stream-filter": "^2.1.0", "stream-reduce": "^1.0.3", @@ -50305,7 +50305,7 @@ "node-fetch": "^2.6.0", "node-loader": "^0.6.0", "reflect-metadata": "^0.1.13", - "rotating-file-stream": "^2.0.2", + "rotating-file-stream": "^3.2.1", "shx": "^0.3.3", "sqlite3": "^5.1.2", "stream-filter": "^2.1.0", @@ -76173,9 +76173,9 @@ } }, "rotating-file-stream": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-2.1.6.tgz", - "integrity": "sha512-qS0ndAlDu80MMXeRonqGMXslF0FErzcUSbcXhus3asRG4cvCS79hc5f7s0x4bPAsH6wAwyHVIeARg69VUe3JmQ==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-3.2.1.tgz", + "integrity": "sha512-n2B18CJb+n2VA5Tdle+1NP2toEcRv68CjAOBjHmwcyswNwMVsrN3gVRZ9ymH3sapaiGY8jc9OhhV5b6I5rAeiA==" }, "run-async": { "version": "2.4.1", diff --git a/packages/main/package.json b/packages/main/package.json index 815aa4970a..653eca359f 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -46,7 +46,7 @@ "music-metadata": "^7.12.3", "node-fetch": "^2.6.0", "reflect-metadata": "^0.1.13", - "rotating-file-stream": "^2.0.2", + "rotating-file-stream": "^3.2.1", "sqlite3": "^5.1.2", "stream-filter": "^2.1.0", "stream-reduce": "^1.0.3", @@ -96,4 +96,4 @@ "mpris-service": "2.1.0", "x11": "^2.3.0" } -} \ No newline at end of file +} diff --git a/packages/main/src/services/logger/index.ts b/packages/main/src/services/logger/index.ts index 8f7504feff..e1c84b9675 100644 --- a/packages/main/src/services/logger/index.ts +++ b/packages/main/src/services/logger/index.ts @@ -2,17 +2,10 @@ import { app } from 'electron'; import timber from 'electron-timber'; import path from 'path'; -import * as rts from 'rotating-file-stream'; +import {RotatingFileStream, createStream} from 'rotating-file-stream'; timber.hookConsole(); -const errorLogStream = rts.createStream( - path.join(app.getPath('userData'), 'logs', 'nuclear-error.log'), - { - size: '5M', - compress: 'gzip' - } -); /** * @see {@link https://github.com/sindresorhus/electron-timber} @@ -34,10 +27,19 @@ export interface ILogger { class Logger implements ILogger { private logger: typeof timber; private name: string; + private errorLogStream: RotatingFileStream; constructor(name?: string) { this.name = name || 'main'; this.logger = name ? timber.create({ name }) : timber; + this.errorLogStream = createStream( + path.join(app.getPath('userData'), 'logs', 'nuclear-error.log'), + { + size: '1M', + compress: 'gzip', + rotate: 5 // arbitrary value to specify the number of rotations + } + ); } private getDate() { @@ -54,15 +56,19 @@ class Logger implements ILogger { writeToFile(name: string, ...args: any[]) { args.forEach((log) => { if (log.stack) { - errorLogStream.write(`${this.getDate()}${name} > ${log.stack}`); + this.errorLogStream.write(`${this.getDate()}${name} > ${log.stack}`); } else if (log.message) { - errorLogStream.write(`${this.getDate()}${name} > ${log.message}\n`); + this.errorLogStream.write(`${this.getDate()}${name} > ${log.message}\n`); } else if (log.toString) { - errorLogStream.write(`${this.getDate()}${name} > ${log.toString()}\n`); + this.errorLogStream.write(`${this.getDate()}${name} > ${log.toString()}\n`); } }); } + getErrorLogStream() { + return this.errorLogStream; + } + log(...args: any[]): void { this.logger.log(...args); } diff --git a/packages/main/src/services/logger/logger.test.ts b/packages/main/src/services/logger/logger.test.ts new file mode 100644 index 0000000000..5cf2b930d3 --- /dev/null +++ b/packages/main/src/services/logger/logger.test.ts @@ -0,0 +1,71 @@ +import Logger from '.'; + +jest.mock('electron-timber', () => ({ + error: jest.fn(), + hookConsole: jest.fn() +})); + +jest.mock('electron', () => ({ + app: { + getPath: jest.fn().mockImplementation(() => '/user/data') + } +})); + + +jest.mock('fs/promises', () => { + const writeFileMock = jest.fn(); + const closeFileMock = jest.fn(); + return ({ + open: jest.fn().mockResolvedValue({ + write: jest.fn(), + close: jest.fn() + }), + stat: jest.fn().mockResolvedValue({ + isFile: jest.fn().mockReturnValue(true), + size: 0 + }), + writeFile: jest.fn().mockResolvedValue({}), + mkdir: jest.fn().mockResolvedValue({}), + closeFileMock, + writeFileMock + }); +}); + +jest.mock('fs', () => ({ + constants: jest.requireActual('fs').constants, + access: jest.fn().mockResolvedValue({}), + createReadStream: jest.fn(() => ({ + once: jest.fn(), + pipe: jest.fn(() => ({ + ...jest.requireActual('stream').Writable.prototype, + write: jest.fn(), + end: jest.fn(), + emit: jest.fn() + })) + })), + createWriteStream: jest.fn(() => ({ + once: jest.fn(), + emit: jest.fn() + })) +})); + +describe('logger', () => { + let logger: Logger; + + it('creates a file for logs', async () => { + logger = new Logger(); + const errorLogStream = logger.getErrorLogStream(); + await new Promise((resolve) => { + errorLogStream.on('open', () => { + resolve(0); + } + ); + }); + + const fsPromises = await import('fs/promises'); + logger.error(new Error('test error')); + + expect(fsPromises.stat).toHaveBeenCalledWith('/user/data/logs/nuclear-error.log'); + expect(fsPromises.open).toHaveBeenCalledWith('/user/data/logs/nuclear-error.log', 'a', undefined); + }); +});