From 522fee8b89a4e8ced58ec95194509ffa1e79b3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lobo=20Metal=C3=BArgico?= <43734867+LoboMetalurgico@users.noreply.github.com> Date: Thu, 4 May 2023 09:43:00 -0300 Subject: [PATCH 1/2] (feat): WIP log file generation system Co-Authored-By: Space_Interprise <44732812+emanuelfranklyn@users.noreply.github.com> --- package-lock.json | 37 +++- package.json | 2 + src/main/interfaces/ILoggerOption.ts | 12 ++ src/main/logger.ts | 303 ++++++++++++++++----------- src/tests/tester.js | 19 +- 5 files changed, 242 insertions(+), 131 deletions(-) diff --git a/package-lock.json b/package-lock.json index ddb4ece..dd5cc48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.1", "license": "MIT", "dependencies": { + "@types/adm-zip": "^0.5.0", + "adm-zip": "^0.5.10", "chalk": "^4.1.2" }, "devDependencies": { @@ -150,6 +152,14 @@ "node": ">= 8" } }, + "node_modules/@types/adm-zip": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz", + "integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -165,8 +175,7 @@ "node_modules/@types/node": { "version": "18.15.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", - "dev": true + "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" }, "node_modules/@types/semver": { "version": "7.3.13", @@ -383,6 +392,14 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/adm-zip": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2971,6 +2988,14 @@ "fastq": "^1.6.0" } }, + "@types/adm-zip": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz", + "integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==", + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2986,8 +3011,7 @@ "@types/node": { "version": "18.15.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", - "dev": true + "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" }, "@types/semver": { "version": "7.3.13", @@ -3107,6 +3131,11 @@ "dev": true, "requires": {} }, + "adm-zip": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", diff --git a/package.json b/package.json index ea148b0..c0e77fb 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ }, "homepage": "https://github.com/PromisePending/logger.js#readme", "dependencies": { + "@types/adm-zip": "^0.5.0", + "adm-zip": "^0.5.10", "chalk": "^4.1.2" }, "devDependencies": { diff --git a/src/main/interfaces/ILoggerOption.ts b/src/main/interfaces/ILoggerOption.ts index 4aa2b8c..f0e41c1 100644 --- a/src/main/interfaces/ILoggerOption.ts +++ b/src/main/interfaces/ILoggerOption.ts @@ -11,6 +11,17 @@ export enum ELoggerLevel { DEBUG = 4 } +export interface ILoggerFileProperties { + enable: boolean; + logFolderPath: string; + enableLatestLog?: boolean; + enableDebugLog?: boolean; + enableErrorLog?: boolean; + enableFatalLog?: boolean; + generateHTMLLog?: boolean; + compressLogFilesAfterNewExecution?: boolean; +} + export interface ILoggerOptions { defaultLevel?: ELoggerLevel; prefix?: string; @@ -18,4 +29,5 @@ export interface ILoggerOptions { coloredBackground?: boolean; allLineColored?: boolean; disableFatalCrash?: boolean; + fileProperties?: ILoggerFileProperties } diff --git a/src/main/logger.ts b/src/main/logger.ts index 9582705..3459e49 100644 --- a/src/main/logger.ts +++ b/src/main/logger.ts @@ -1,16 +1,26 @@ -import { ILoggerOptions, ELoggerLevel } from './interfaces'; +import { ILoggerOptions, ELoggerLevel, ILoggerFileProperties } from './interfaces'; import chalk from 'chalk'; +import Path from 'path'; +import fs from 'fs'; +import admZip from 'adm-zip'; export class Logger { private defaultLevel: ELoggerLevel = ELoggerLevel.LOG; private debugActive = false; private prefix?: string; private coloredBackground: boolean; - private colors: any; + private BaseColors: any; + private AlternateColors: any; private disableFatalCrash: boolean; private allLineColored: boolean; - - constructor({ prefix, debug, defaultLevel, coloredBackground, disableFatalCrash, allLineColored }: ILoggerOptions) { + private fileProperties: ILoggerFileProperties; + private latestFileStream?: fs.WriteStream; + private debugLogStream?: fs.WriteStream; + private errorLogStream?: fs.WriteStream; + private htmlBackgroundColor: string; + private htmlTextColor: string; + + constructor({ prefix, debug, defaultLevel, coloredBackground, disableFatalCrash, allLineColored, fileProperties }: ILoggerOptions) { this.prefix = prefix ?? ''; this.debugActive = debug ?? false; this.defaultLevel = defaultLevel ?? ELoggerLevel.INFO; @@ -18,13 +28,114 @@ export class Logger { this.disableFatalCrash = disableFatalCrash ?? false; this.allLineColored = allLineColored ?? false; - this.colors = { - info: '#cc80ff', - warn: '#ff8a1c', - error: '#ff4a4a', + this.htmlBackgroundColor = '#0a002b'; + this.htmlTextColor = '#ffffff'; + + this.fileProperties = { + enable: false, + logFolderPath: Path.join(__dirname, 'logs'), + enableLatestLog: true, + enableDebugLog: false, + enableErrorLog: false, + enableFatalLog: true, + generateHTMLLog: false, + compressLogFilesAfterNewExecution: true, + }; + + this.fileProperties = { ...this.fileProperties, ...fileProperties ?? {} }; + + /** + * log (root) + * - fatal-crash/ + * - fatal-dataDoCrash.log + * - latestLogs/ + * - debug.log (contém tudo, incluindo o debug) + * - error.log (contém apenas os erros) + * - logs-.zip (contém todos os logs gerados na sessão anterior) + * - latest.log (contém tudo, exceto fatal (apenas resumo, sem stacktrace) e debug (exceto se o debug estiver ativado)) + */ + if (this.fileProperties.enable) { + // create log folder if not exists + if (!fs.existsSync(this.fileProperties.logFolderPath)) fs.mkdirSync(this.fileProperties.logFolderPath); + else this.compressLastSessionLogs(); + + // creates folders for fatal-crash and latest logs + if (!fs.existsSync(Path.join(this.fileProperties.logFolderPath, 'fatal-crash'))) fs.mkdirSync(Path.join(this.fileProperties.logFolderPath, 'fatal-crash')); + if (!fs.existsSync(Path.join(this.fileProperties.logFolderPath, 'latestLogs'))) fs.mkdirSync(Path.join(this.fileProperties.logFolderPath, 'latestLogs')); + + // eslint-disable-next-line max-len + const defaultHeader = `\n`; + + if (this.fileProperties.enableLatestLog) { + this.latestFileStream = fs.createWriteStream( + Path.join(this.fileProperties.logFolderPath, `latest.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, + ); + this.latestFileStream.write(defaultHeader); + } + if (this.fileProperties.enableDebugLog) { + this.debugLogStream = fs.createWriteStream( + Path.join(this.fileProperties.logFolderPath, 'latestLogs', `debug.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, + ); + this.debugLogStream.write(defaultHeader); + } + if (this.fileProperties.enableErrorLog) { + this.errorLogStream = fs.createWriteStream( + Path.join(this.fileProperties.logFolderPath, 'latestLogs', `error.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, + ); + this.errorLogStream.write(defaultHeader); + } + } else { + this.fileProperties.enableLatestLog = false; + this.fileProperties.enableDebugLog = false; + this.fileProperties.enableErrorLog = false; + this.fileProperties.enableFatalLog = false; + this.fileProperties.generateHTMLLog = false; + this.fileProperties.compressLogFilesAfterNewExecution = false; + } + + // color for text or colored background + this.BaseColors = { + [ELoggerLevel.INFO]: '#cc80ff', + [ELoggerLevel.WARN]: '#ff8a1c', + [ELoggerLevel.ERROR]: '#ff4a4a', + [ELoggerLevel.DEBUG]: '#555555', + }; + + // color for text on colored background + this.AlternateColors = { + [ELoggerLevel.INFO]: '#000000', + [ELoggerLevel.WARN]: '#000000', + [ELoggerLevel.ERROR]: '#000000', + [ELoggerLevel.DEBUG]: '#D4D4D4', }; } + private compressLastSessionLogs(): void { + if (!this.fileProperties.compressLogFilesAfterNewExecution) return; + + const zip = new admZip(); + + var files = fs.readdirSync(this.fileProperties.logFolderPath); + const fatalCrashFiles = fs.readdirSync(Path.join(this.fileProperties.logFolderPath, 'fatal-crash')); + const latestLogsFiles = fs.readdirSync(Path.join(this.fileProperties.logFolderPath, 'latestLogs')); + files = files.concat(fatalCrashFiles.map((file) => Path.join('fatal-crash', file))); + files = files.concat(latestLogsFiles.map((file) => Path.join('latestLogs', file))); + files.forEach((file) => { + if (file.endsWith('.log') || file.endsWith('.html')) { + zip.addLocalFile(Path.join(this.fileProperties.logFolderPath, file)); + // don't delete fatal-crash logs + if (!file.startsWith('fatal')) fs.unlinkSync(Path.join(this.fileProperties.logFolderPath, file)); + } + }); + + const date = new Date(Date.now()); + const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + const month = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); + const year = date.getFullYear(); + + fs.writeFileSync(Path.resolve(this.fileProperties.logFolderPath, `logs-${year}-${month}-${day}-${this.getTime(true)}.zip`), zip.toBuffer()); + } + private getFormattedPrefix(): string { var prefix = ''; prefix += chalk.hex('#5c5c5c')('['); @@ -34,115 +145,92 @@ export class Logger { return this.prefix !== '' ? prefix : ''; } - private getTime(): string { + private getTime(friendlySymbols?: boolean): string { const time = new Date(Date.now()); const seconds = time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds(); const minutes = time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes(); const hours = time.getHours() < 10 ? '0' + time.getHours() : time.getHours(); - return `[${hours}:${minutes}:${seconds}]`; + return `${friendlySymbols ? '' : '['}${hours}${friendlySymbols ? '-' : ':'}${minutes}${friendlySymbols ? '-' : ':'}${seconds}${friendlySymbols ? '' : ']'}`; } - info(text: string | number | Error, ...args: any): void { - var textConstructor = ''; - textConstructor += this.coloredBackground ? this.getTime() : chalk.hex(this.colors.info)(this.getTime()); - textConstructor += this.coloredBackground ? (this.prefix ? `[${this.prefix}]` : '') : this.getFormattedPrefix(); - - textConstructor += ' Info:'; + private generateMessagePrefix(level: ELoggerLevel): { colored: string; raw: string, txtColor: string } { + const fgColor = [this.BaseColors[level], this.AlternateColors[level]]; + var time = chalk.hex(fgColor[Number(this.coloredBackground)])(this.getTime() + ' '); + var prefix = chalk.hex(fgColor[Number(this.coloredBackground)])(this.getFormattedPrefix() + ' '); + var levelText = chalk.hex(fgColor[Number(this.coloredBackground)])(ELoggerLevel[level].toUpperCase() + ':'); if (this.coloredBackground) { - textConstructor = chalk.bgHex(this.colors.info)(chalk.black(textConstructor)); - } - - if (this.allLineColored) { - if (this.coloredBackground) { - textConstructor += chalk.bgHex(this.colors.info)(chalk.black(' ' + text)); - } else { - textConstructor += ' ' + chalk.hex(this.colors.info)(text); - } - } else { - textConstructor += ' ' + text; + time = chalk.bgHex(this.BaseColors[level])(time); + prefix = chalk.bgHex(this.BaseColors[level])(prefix); + levelText = chalk.bgHex(this.BaseColors[level])(levelText); } - if ((!args && !(args instanceof Boolean)) || ((args instanceof Array) && args.length === 0)) { - console.log(textConstructor); - } else { - console.log(textConstructor, args); - } + return { + colored: `${time}${prefix}${levelText}`, + raw: `${this.getTime()} [${this.prefix}] ${ELoggerLevel[level].toUpperCase()}:`, + txtColor: fgColor[Number(this.coloredBackground)], + }; } - warn(text: string | number | Error, ...args: any): void { - var textConstructor = ''; - textConstructor += this.coloredBackground ? this.getTime() : chalk.hex(this.colors.warn)(this.getTime()); - textConstructor += this.coloredBackground ? (this.prefix ? `[${this.prefix}]` : '') : this.getFormattedPrefix(); + log(text: string | number | Error, levelToLog?: ELoggerLevel, ...args: any): void { + const level = levelToLog ?? this.defaultLevel; + if (level === ELoggerLevel.FATAL) return this.fatal(text, ...args); + const consoleLevels = { + [ELoggerLevel.INFO]: console.log, + [ELoggerLevel.WARN]: console.warn, + [ELoggerLevel.ERROR]: console.error, + [ELoggerLevel.DEBUG]: console.debug, + }; - textConstructor += ` ${text.toString().toLowerCase().split(' ')[0].includes('warn') ? '' : 'Warn:'}`; + const { colored, raw, txtColor } = this.generateMessagePrefix(level); - if (this.coloredBackground) { - textConstructor = chalk.bgHex(this.colors.warn)(chalk.black(textConstructor)); - } + const msg = (this.coloredBackground && this.allLineColored) + ? chalk.bgHex(this.BaseColors[level])(chalk.hex(this.AlternateColors[level])(' ' + text)) + : (this.allLineColored ? chalk.hex(this.BaseColors[level])(' ' + text) : ' ' + text) + ; - if (this.allLineColored) { - if (this.coloredBackground) { - textConstructor += chalk.bgHex(this.colors.warn)(chalk.black(' ' + text)); - } else { - textConstructor += ' ' + chalk.hex(this.colors.warn)(text); - } - } else { - textConstructor += ' ' + text; - } + consoleLevels[level](colored + msg, ...args); - if ((!args && !(args instanceof Boolean)) || ((args instanceof Array) && args.length === 0)) { - console.warn(textConstructor); - } else { - console.warn(textConstructor, args); + // eslint-disable-next-line max-len + const textSpan = this.allLineColored ? `${text}` : `${text}`; + // eslint-disable-next-line max-len + const prefixSpan = `${raw} ${textSpan}\n`; + if (this.fileProperties.enableDebugLog) { + this.debugLogStream?.write(this.fileProperties.generateHTMLLog ? prefixSpan : (raw + ' ' + text + '\n')); } } - error(text: string | number | Error, ...args: any): void { - var textConstructor = ''; - textConstructor += this.coloredBackground ? this.getTime() : chalk.hex(this.colors.error)(this.getTime()); - textConstructor += this.coloredBackground ? (this.prefix ? `[${this.prefix}]` : '') : this.getFormattedPrefix(); + info(text: string | number | Error, ...args: any): void { + this.log(text, ELoggerLevel.INFO, ...args); + } - textConstructor += `${text.toString().toLowerCase().split(' ')[0].includes('error') ? '' : ' Error:'}`; + warn(text: string | number | Error, ...args: any): void { + this.log(text, ELoggerLevel.WARN, ...args); + } - if (this.coloredBackground) { - textConstructor = chalk.bgHex(this.colors.error)(chalk.black(textConstructor)); - } + error(text: string | number | Error, ...args: any): void { + this.log(text, ELoggerLevel.ERROR, ...args); + } - if (this.allLineColored) { - if (this.coloredBackground) { - textConstructor += chalk.bgHex(this.colors.error)(chalk.black(' ' + text)); - } else { - textConstructor += ' ' + chalk.hex(this.colors.error)(text); + fatal(text: string | number | Error, ...args: any): void { + var message = text.toString(); + if (text instanceof Error) { + // create stacktrace + const stack = text.stack?.split('\n'); + if (stack) { + message = stack[0]; } - } else { - textConstructor += ' ' + text; } - if ((!args && !(args instanceof Boolean)) || ((args instanceof Array) && args.length === 0)) { - console.error(textConstructor); - } else { - console.error(textConstructor, args); - } - } - - fatal(text: string | number | Error, ...args: any): void { - var textConstructor = ''; - textConstructor += chalk.hex('#ff5647')(this.getTime()); - textConstructor += this.getFormattedPrefix(); - textConstructor += ` Fatal${text.toString().toLowerCase().split(' ')[0].includes('error') ? '' : ':'} `; - textConstructor += text.toString().split('\n')[0]; + const time = this.getTime(); + const prefix = this.getFormattedPrefix(); + const levelMsg = text.toString().startsWith('Error') ? 'FATAL ' : 'FATAL ERROR: '; - textConstructor = chalk.bgWhite(chalk.red(textConstructor)); + message = `${time} ${prefix} ${levelMsg} ${message.toString()}`; - textConstructor += '\n'; - textConstructor += text.toString().split('\n').slice(1).join('\n'); + const msg = chalk.bgWhite(chalk.redBright(message)); - if ((!args && !(args instanceof Boolean)) || ((args instanceof Array) && args.length === 0)) { - console.error(textConstructor); - } else { - console.error(textConstructor, args); - } + console.error(msg, ...args); if (!this.disableFatalCrash) { process.exit(5); @@ -151,41 +239,6 @@ export class Logger { debug(text: string | number | Error, ...args: any): void { if (!this.debugActive) return; - var textConstructor = ''; - textConstructor += chalk.hex('#555555')(this.getTime()); - textConstructor += this.getFormattedPrefix(); - textConstructor += chalk.hex('#555555')(`${text.toString().toLowerCase().split(' ')[0].includes('debug') ? '' : ' Debug:'} `); - textConstructor += chalk.hex('#555555')(text); - - if (((!args && !(args instanceof Boolean)) || ((args instanceof Array) && args.length === 0)) || ((args instanceof Array) && args.length === 0)) { - console.debug(textConstructor); - } else { - console.debug(textConstructor, args); - } - } - - log(message: string | number | Error, level?: ELoggerLevel, ...args: any): void { - level = level ?? this.defaultLevel; - switch (level) { - case ELoggerLevel.DEBUG: - this.debug(message, args); - break; - case ELoggerLevel.WARN: - case ELoggerLevel.ALERT: - this.warn(message, args); - break; - case ELoggerLevel.ERROR: - case ELoggerLevel.SEVERE: - this.error(message, args); - break; - case ELoggerLevel.FATAL: - this.fatal(message, args); - break; - case ELoggerLevel.INFO: - case ELoggerLevel.LOG: - default: - this.info(message, args); - break; - } + this.log(text, ELoggerLevel.DEBUG, ...args); } } diff --git a/src/tests/tester.js b/src/tests/tester.js index a6ff3eb..03ccaf8 100644 --- a/src/tests/tester.js +++ b/src/tests/tester.js @@ -1,8 +1,23 @@ const logger = require('../../build'); -const log = new logger.Logger({ debug: true, disableFatalCrash: true }); +const log = new logger.Logger({ + prefix: 'TESTER', + debug: true, + coloredBackground: true, + allLineColored: true, + disableFatalCrash: true, + fileProperties: { + enable: true, + logFolderPath: './logs1', + enableLatestLog: true, + enableDebugLog: true, + enableErrorLog: true, + enableFatalLog: true, + generateHTMLLog: false, + compressLogFilesAfterNewExecution: true, + }, +}); -log.log('Hello world!'); log.debug('Hello world!'); log.info('Hello world!'); log.warn('Hello world!'); From d8c6119f1e9d24481c5a6962c30c0e54b8b49307 Mon Sep 17 00:00:00 2001 From: Space_Interprise <44732812+emanuelfranklyn@users.noreply.github.com> Date: Sun, 7 May 2023 22:08:44 -0300 Subject: [PATCH 2/2] (feat): Finish V1.1 --- CHANGELOG.md | 27 ++ README.md | 18 +- package-lock.json | 470 ++++++++++++++------------- package.json | 8 +- src/main/autoLogEnd.ts | 29 +- src/main/index.ts | 2 +- src/main/interfaces/ILoggerOption.ts | 26 ++ src/main/logger.ts | 202 ++++++++---- src/tests/tester.js | 17 +- 9 files changed, 485 insertions(+), 314 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b17d8b4..f5adc5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# V1.1 + - Added support for logging to a file + + - [NEW] Added the `fileProperties` parameter to the `Logger` constructor which includes the following properties: + + - enable: enable logging to a file (defaults to `false`) [If `false` all the other properties will be ignored] + + - logFolderPath: path to the folder where the log files will be stored (will be created if it doesn't exist, defaults to `./logs`) + + - enableLatestLog?: if true, the latest log file will be stored in the `logFolderPath` with the name `latest.log` (defaults to `true`) + + - enableDebugLog?: if true, the debug log will be stored in the `logFolderPath` inside a folder named `latestLogs` with the name `debug.log` (defaults to `true`) + + - enableErrorLog?: if true, the error log will be stored in the `logFolderPath` inside a folder named `latestLogs` with the name `error.log` (defaults to `false`) + + - enableFatalLog?: if true, the fatal log will be stored in the `logFolderPath` inside a folder named `fatal-crash` with the name `fatal-DATE.log` (defaults to `true`) + + - generateHTMLLog?: if true, the log files will be generated in HTML format (their extension will change from .log to .html) (defaults to `false`) + + - compressLogFilesAfterNewExecution?: if true, the log files will be compressed to a zip file after a new execution of the program (defaults to `true`) + + - [NEW] Added support to log objects, arrays and etc. Like the default `console.log` function. + + - [BREAKING] Changed the export of AutoLogEnd now importing the package will return the `Logger` class and a object named `AutoLogEnd` with the `activate` and `deactivate` functions. + + - [NEW] AutoLogEnd `activate` function now accepts a `Logger` instance as 2º parameter. If not passed it will create a new instance of `Logger` with the default parameters. otherwise it will use the passed instance. + # V1.0 - Initial release of logger diff --git a/README.md b/README.md index 1ea8583..1bb7259 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,19 @@ import { Logger } from '@promisepending/logger.js'; const logger = new Logger({ prefix: 'Logger.JS', // This will be the prefix of all logs (default: null) disableFatalCrash: true, // If true, the logger will not crash the process when a fatal error occurs (default: false) - // ... + allLineColored: true, // If true, the whole line will be colored instead of only the prefix (default: false) + coloredBackground: false, // If true, the background of the lines will be colored instead of the text (default: false) + debug: false, // If true, the logger will log debug messages (default: false) + fileProperties: { // This is the configuration of the log files + enable: true, // If true, the logger will log to files (default: false) [NOTE: If false all below options will be ignored] + logFolderPath: './logs', // This is the path of the folder where the log files will be saved (default: './logs') + enableLatestLog: true, // If true, the logger will save the latest log in a file (default: true) + enableDebugLog: true, // If true, the logger will save the debug logs in a file (default: false) + enableErrorLog: true, // If true, the logger will save the error logs in a file (default: false) + enableFatalLog: true, // If true, the logger will save the fatal logs in a file (default: true) + generateHTMLLog: true, // If true, the logger will generate a HTML file with the logs otherwise a .log file (default: false) + compressLogFilesAfterNewExecution: true, // If true, the logger will compress the log files to zip after a new execution (default: true) + }, }); ``` @@ -128,6 +140,4 @@ logger.fatal('This is a fatal message'); # -

Made with 💚 By PromisePending™'s team.

- -

All Reserved Rights.

+

Made with 💜 By PromisePending™'s team.

diff --git a/package-lock.json b/package-lock.json index dd5cc48..6d5c7d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,21 @@ { "name": "@promisepending/logger.js", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@promisepending/logger.js", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "dependencies": { - "@types/adm-zip": "^0.5.0", "adm-zip": "^0.5.10", - "chalk": "^4.1.2" + "chalk": "^4.1.2", + "escape-html": "^1.0.3" }, "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/escape-html": "^1.0.2", "@types/node": "^18.15.3", "@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/parser": "^5.48.2", @@ -29,9 +31,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", - "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" @@ -44,23 +46,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", - "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -76,9 +78,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", + "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -156,10 +158,17 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz", "integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==", + "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/escape-html": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.2.tgz", + "integrity": "sha512-gaBLT8pdcexFztLSPRtriHeXY/Kn4907uOCZ4Q3lncFBkheAWOuNt53ypsF8szgxbEJ513UeBzcf4utN0EzEwA==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -173,9 +182,10 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" + "version": "18.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.5.tgz", + "integrity": "sha512-seOA34WMo9KB+UA78qaJoCO20RJzZGVXQ5Sh6FWu0g/hfT44nKXnej3/tCQl7FL97idFpBhisLYCTB50S0EirA==", + "dev": true }, "node_modules/@types/semver": { "version": "7.3.13", @@ -184,15 +194,15 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz", - "integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.2.tgz", + "integrity": "sha512-yVrXupeHjRxLDcPKL10sGQ/QlVrA8J5IYOEWVqk0lJaSZP7X5DfnP7Ns3cc74/blmbipQ1htFNVGsHX6wsYm0A==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/type-utils": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/scope-manager": "5.59.2", + "@typescript-eslint/type-utils": "5.59.2", + "@typescript-eslint/utils": "5.59.2", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -218,14 +228,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz", - "integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.2.tgz", + "integrity": "sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.59.2", + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/typescript-estree": "5.59.2", "debug": "^4.3.4" }, "engines": { @@ -245,13 +255,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz", - "integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.2.tgz", + "integrity": "sha512-dB1v7ROySwQWKqQ8rEWcdbTsFjh2G0vn8KUyvTXdPoyzSL6lLGkiXEV5CvpJsEe9xIdKV+8Zqb7wif2issoOFA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0" + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/visitor-keys": "5.59.2" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -262,13 +272,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz", - "integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.2.tgz", + "integrity": "sha512-b1LS2phBOsEy/T381bxkkywfQXkV1dWda/z0PhnIy3bC5+rQWQDS7fk9CSpcXBccPY27Z6vBEuaPBCKCgYezyQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/typescript-estree": "5.59.2", + "@typescript-eslint/utils": "5.59.2", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -289,9 +299,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz", - "integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.2.tgz", + "integrity": "sha512-LbJ/HqoVs2XTGq5shkiKaNTuVv5tTejdHgfdjqRUGdYhjW1crm/M7og2jhVskMt8/4wS3T1+PfFvL1K3wqYj4w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -302,13 +312,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz", - "integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.2.tgz", + "integrity": "sha512-+j4SmbwVmZsQ9jEyBMgpuBD0rKwi9RxRpjX71Brr73RsYnEr3Lt5QZ624Bxphp8HUkSKfqGnPJp1kA5nl0Sh7Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0", + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/visitor-keys": "5.59.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -329,17 +339,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz", - "integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.2.tgz", + "integrity": "sha512-kSuF6/77TZzyGPhGO4uVp+f0SBoYxCDf+lW3GKhtKru/L8k/Hd7NFQxyWUeY7Z/KGB2C6Fe3yf2vVi4V9TsCSQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.59.2", + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/typescript-estree": "5.59.2", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -355,12 +365,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz", - "integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.2.tgz", + "integrity": "sha512-EEpsO8m3RASrKAHI9jpavNv9NlEUebV4qmF1OWxSTtKSFBpC1NCmWazDQHFivRf0O1DV11BA645yrLEVQ0/Lig==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.55.0", + "@typescript-eslint/types": "5.59.2", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -796,6 +806,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -809,15 +824,15 @@ } }, "node_modules/eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.40.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -827,9 +842,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -866,9 +881,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", - "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -924,9 +939,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -1055,9 +1070,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz", - "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==", + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", + "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "dev": true, "peer": true, "dependencies": { @@ -1228,18 +1243,21 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -1247,6 +1265,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -1259,14 +1280,14 @@ } }, "node_modules/espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1832,9 +1853,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -2020,9 +2041,9 @@ "dev": true }, "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", "dev": true, "funding": { "type": "opencollective", @@ -2357,9 +2378,9 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -2413,14 +2434,14 @@ ] }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -2442,12 +2463,12 @@ } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -2530,9 +2551,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2779,9 +2800,9 @@ } }, "node_modules/typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2902,29 +2923,29 @@ }, "dependencies": { "@eslint-community/eslint-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", - "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "requires": { "eslint-visitor-keys": "^3.3.0" } }, "@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", "dev": true }, "@eslint/eslintrc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", - "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -2934,9 +2955,9 @@ } }, "@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", + "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", "dev": true }, "@humanwhocodes/config-array": { @@ -2992,10 +3013,17 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.0.tgz", "integrity": "sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==", + "dev": true, "requires": { "@types/node": "*" } }, + "@types/escape-html": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.2.tgz", + "integrity": "sha512-gaBLT8pdcexFztLSPRtriHeXY/Kn4907uOCZ4Q3lncFBkheAWOuNt53ypsF8szgxbEJ513UeBzcf4utN0EzEwA==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -3009,9 +3037,10 @@ "dev": true }, "@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" + "version": "18.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.5.tgz", + "integrity": "sha512-seOA34WMo9KB+UA78qaJoCO20RJzZGVXQ5Sh6FWu0g/hfT44nKXnej3/tCQl7FL97idFpBhisLYCTB50S0EirA==", + "dev": true }, "@types/semver": { "version": "7.3.13", @@ -3020,15 +3049,15 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz", - "integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.2.tgz", + "integrity": "sha512-yVrXupeHjRxLDcPKL10sGQ/QlVrA8J5IYOEWVqk0lJaSZP7X5DfnP7Ns3cc74/blmbipQ1htFNVGsHX6wsYm0A==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/type-utils": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/scope-manager": "5.59.2", + "@typescript-eslint/type-utils": "5.59.2", + "@typescript-eslint/utils": "5.59.2", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -3038,53 +3067,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz", - "integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.2.tgz", + "integrity": "sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.59.2", + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/typescript-estree": "5.59.2", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz", - "integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.2.tgz", + "integrity": "sha512-dB1v7ROySwQWKqQ8rEWcdbTsFjh2G0vn8KUyvTXdPoyzSL6lLGkiXEV5CvpJsEe9xIdKV+8Zqb7wif2issoOFA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0" + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/visitor-keys": "5.59.2" } }, "@typescript-eslint/type-utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz", - "integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.2.tgz", + "integrity": "sha512-b1LS2phBOsEy/T381bxkkywfQXkV1dWda/z0PhnIy3bC5+rQWQDS7fk9CSpcXBccPY27Z6vBEuaPBCKCgYezyQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.55.0", - "@typescript-eslint/utils": "5.55.0", + "@typescript-eslint/typescript-estree": "5.59.2", + "@typescript-eslint/utils": "5.59.2", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz", - "integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.2.tgz", + "integrity": "sha512-LbJ/HqoVs2XTGq5shkiKaNTuVv5tTejdHgfdjqRUGdYhjW1crm/M7og2jhVskMt8/4wS3T1+PfFvL1K3wqYj4w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz", - "integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.2.tgz", + "integrity": "sha512-+j4SmbwVmZsQ9jEyBMgpuBD0rKwi9RxRpjX71Brr73RsYnEr3Lt5QZ624Bxphp8HUkSKfqGnPJp1kA5nl0Sh7Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/visitor-keys": "5.55.0", + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/visitor-keys": "5.59.2", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3093,28 +3122,28 @@ } }, "@typescript-eslint/utils": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz", - "integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.2.tgz", + "integrity": "sha512-kSuF6/77TZzyGPhGO4uVp+f0SBoYxCDf+lW3GKhtKru/L8k/Hd7NFQxyWUeY7Z/KGB2C6Fe3yf2vVi4V9TsCSQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.55.0", - "@typescript-eslint/types": "5.55.0", - "@typescript-eslint/typescript-estree": "5.55.0", + "@typescript-eslint/scope-manager": "5.59.2", + "@typescript-eslint/types": "5.59.2", + "@typescript-eslint/typescript-estree": "5.59.2", "eslint-scope": "^5.1.1", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz", - "integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==", + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.2.tgz", + "integrity": "sha512-EEpsO8m3RASrKAHI9jpavNv9NlEUebV4qmF1OWxSTtKSFBpC1NCmWazDQHFivRf0O1DV11BA645yrLEVQ0/Lig==", "dev": true, "requires": { - "@typescript-eslint/types": "5.55.0", + "@typescript-eslint/types": "5.59.2", "eslint-visitor-keys": "^3.3.0" } }, @@ -3433,6 +3462,11 @@ "is-symbol": "^1.0.2" } }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3440,15 +3474,15 @@ "dev": true }, "eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.40.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3458,9 +3492,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3488,9 +3522,9 @@ }, "dependencies": { "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -3506,9 +3540,9 @@ } }, "eslint-config-prettier": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", - "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "requires": {} }, @@ -3542,9 +3576,9 @@ } }, "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "requires": { "debug": "^3.2.7" @@ -3641,9 +3675,9 @@ } }, "eslint-plugin-n": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz", - "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==", + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", + "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "dev": true, "peer": true, "requires": { @@ -3750,20 +3784,20 @@ } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true }, "espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esquery": { @@ -4179,9 +4213,9 @@ "dev": true }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -4304,9 +4338,9 @@ "dev": true }, "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", "dev": true }, "js-yaml": { @@ -4550,9 +4584,9 @@ "dev": true }, "prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "prettier-linter-helpers": { @@ -4577,14 +4611,14 @@ "dev": true }, "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" } }, "regexpp": { @@ -4594,12 +4628,12 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -4646,9 +4680,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4823,9 +4857,9 @@ } }, "typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true }, "unbox-primitive": { diff --git a/package.json b/package.json index c0e77fb..0cad410 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@promisepending/logger.js", - "version": "1.0.1", + "version": "1.1.0", "description": "A better logger", "main": "build/index.js", "types": "src/main/index.ts", @@ -24,11 +24,13 @@ }, "homepage": "https://github.com/PromisePending/logger.js#readme", "dependencies": { - "@types/adm-zip": "^0.5.0", "adm-zip": "^0.5.10", - "chalk": "^4.1.2" + "chalk": "^4.1.2", + "escape-html": "^1.0.3" }, "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/escape-html": "^1.0.2", "@types/node": "^18.15.3", "@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/parser": "^5.48.2", diff --git a/src/main/autoLogEnd.ts b/src/main/autoLogEnd.ts index c5cbeaa..736a444 100644 --- a/src/main/autoLogEnd.ts +++ b/src/main/autoLogEnd.ts @@ -2,28 +2,28 @@ import { Logger } from './'; import util from 'util'; var exited = false; +var logger: Logger | null = null; -const logger: Logger = new Logger({ prefix: 'SYSTEM' }); function exitHandler({ err, options, exitCode }: {err?: {stack: any, message: any, name: any}, options?: {uncaughtException: boolean}, exitCode?: number | string}): void { if (!exited) { process.stdin.resume(); exited = true; if (typeof exitCode === 'string') { - logger.warn('Manually Finished!'); + logger?.warn('Manually Finished!'); } else { - if (exitCode !== 123654) logger.info('Program finished, code: ' + exitCode); + if (exitCode !== 123654) logger?.info('Program finished, code: ' + exitCode); if (exitCode === 123654 && options?.uncaughtException) { - logger.fatal(util.format(typeof err === 'string' ? err : err?.stack)); + logger?.fatal(util.format(typeof err === 'string' ? err : err?.stack)); exitCode = 1; } else if (exitCode && exitCode === 123654) { - logger.error(util.format(typeof err === 'string' ? err : err?.stack)); - logger.warn('#===========================================================#'); - logger.warn('| # AutoLogEnd prevent program exit!'); - logger.warn('| # Code that is not async or would be runned after the line that generated the error cannot run as per nodejs default behavior.'); - logger.warn('| # But promises, async code and event based functions will still be executed.'); - logger.warn('| # In order to prevent sync code to stop, use an try-catch or a promise.'); - logger.warn('#===========================================================#'); - logger.warn('If you want to manually exit, you can still use control-c in the process.'); + logger?.error(util.format(typeof err === 'string' ? err : err?.stack)); + logger?.warn('#===========================================================#'); + logger?.warn('| # AutoLogEnd prevent program exit!'); + logger?.warn('| # Code that is not async or would be runned after the line that generated the error cannot run as per nodejs default behavior.'); + logger?.warn('| # But promises, async code and event based functions will still be executed.'); + logger?.warn('| # In order to prevent sync code to stop, use an try-catch or a promise.'); + logger?.warn('#===========================================================#'); + logger?.warn('If you want to manually exit, you can still use control-c in the process.'); exited = false; return; } @@ -32,7 +32,9 @@ function exitHandler({ err, options, exitCode }: {err?: {stack: any, message: an } } -export function activate(uncaughtException?: boolean): void { +export function activate(uncaughtException?: boolean, logger?: Logger): void { + logger = logger ?? new Logger({ prefix: 'SYSTEM' }); + logger?.debug('AutoLogEnd activated!'); process.on('exit', (exitCode) => exitHandler({ exitCode, options: { uncaughtException: false } })); process.on('SIGINT', (error) => { exitHandler({ err: { message: error, name: null, stack: null }, options: { uncaughtException: false }, exitCode: 'SIGINT' }); }); process.on('SIGUSR1', (error) => { exitHandler({ err: { message: error, name: null, stack: null }, options: { uncaughtException: false }, exitCode: 'SIGUSR1' }); }); @@ -54,4 +56,5 @@ export function deactivate(): void { process.removeListener('SIGUSR2', exitHandler); process.removeListener('SIGTERM', exitHandler); process.removeListener('uncaughtException', exitHandler); + logger?.debug('AutoLogEnd deactivated!'); } diff --git a/src/main/index.ts b/src/main/index.ts index 6168fc4..ec4d9ae 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,2 +1,2 @@ -export * from './autoLogEnd'; +export * as AutoLogEnd from './autoLogEnd'; export * from './logger'; diff --git a/src/main/interfaces/ILoggerOption.ts b/src/main/interfaces/ILoggerOption.ts index f0e41c1..425326b 100644 --- a/src/main/interfaces/ILoggerOption.ts +++ b/src/main/interfaces/ILoggerOption.ts @@ -11,6 +11,32 @@ export enum ELoggerLevel { DEBUG = 4 } +export enum ELoggerLevelNames { + 'INFO', + 'WARN', + 'ERROR', + 'FATAL', + 'DEBUG' +} + +// color for text or colored background +export enum ELoggerLevelBaseColors { + '#cc80ff', + '#ff8a1c', + '#ff4a4a', + '#ffffff', + '#555555', +} + +// color for text on colored background +export const ELoggerLevelAlternateColors = [ + '#000000', + '#000000', + '#000000', + '#ff0000', + '#D4D4D4' +] + export interface ILoggerFileProperties { enable: boolean; logFolderPath: string; diff --git a/src/main/logger.ts b/src/main/logger.ts index 3459e49..90a6afb 100644 --- a/src/main/logger.ts +++ b/src/main/logger.ts @@ -1,22 +1,23 @@ -import { ILoggerOptions, ELoggerLevel, ILoggerFileProperties } from './interfaces'; +import { ILoggerOptions, ELoggerLevel, ILoggerFileProperties, ELoggerLevelNames, ELoggerLevelBaseColors, ELoggerLevelAlternateColors } from './interfaces'; +import escape from 'escape-html'; +import admZip from 'adm-zip'; import chalk from 'chalk'; import Path from 'path'; import fs from 'fs'; -import admZip from 'adm-zip'; +import utils from 'util'; export class Logger { private defaultLevel: ELoggerLevel = ELoggerLevel.LOG; private debugActive = false; private prefix?: string; private coloredBackground: boolean; - private BaseColors: any; - private AlternateColors: any; private disableFatalCrash: boolean; private allLineColored: boolean; private fileProperties: ILoggerFileProperties; private latestFileStream?: fs.WriteStream; private debugLogStream?: fs.WriteStream; private errorLogStream?: fs.WriteStream; + private fatalLogStream?: fs.WriteStream; private htmlBackgroundColor: string; private htmlTextColor: string; @@ -44,16 +45,6 @@ export class Logger { this.fileProperties = { ...this.fileProperties, ...fileProperties ?? {} }; - /** - * log (root) - * - fatal-crash/ - * - fatal-dataDoCrash.log - * - latestLogs/ - * - debug.log (contém tudo, incluindo o debug) - * - error.log (contém apenas os erros) - * - logs-.zip (contém todos os logs gerados na sessão anterior) - * - latest.log (contém tudo, exceto fatal (apenas resumo, sem stacktrace) e debug (exceto se o debug estiver ativado)) - */ if (this.fileProperties.enable) { // create log folder if not exists if (!fs.existsSync(this.fileProperties.logFolderPath)) fs.mkdirSync(this.fileProperties.logFolderPath); @@ -64,26 +55,35 @@ export class Logger { if (!fs.existsSync(Path.join(this.fileProperties.logFolderPath, 'latestLogs'))) fs.mkdirSync(Path.join(this.fileProperties.logFolderPath, 'latestLogs')); // eslint-disable-next-line max-len - const defaultHeader = `\n`; + const defaultHeader = `\n`; if (this.fileProperties.enableLatestLog) { this.latestFileStream = fs.createWriteStream( Path.join(this.fileProperties.logFolderPath, `latest.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, ); - this.latestFileStream.write(defaultHeader); + if (this.fileProperties.generateHTMLLog) this.latestFileStream.write(defaultHeader); } if (this.fileProperties.enableDebugLog) { this.debugLogStream = fs.createWriteStream( Path.join(this.fileProperties.logFolderPath, 'latestLogs', `debug.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, ); - this.debugLogStream.write(defaultHeader); + if (this.fileProperties.generateHTMLLog) this.debugLogStream.write(defaultHeader); } if (this.fileProperties.enableErrorLog) { this.errorLogStream = fs.createWriteStream( Path.join(this.fileProperties.logFolderPath, 'latestLogs', `error.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, ); - this.errorLogStream.write(defaultHeader); + if (this.fileProperties.generateHTMLLog) this.errorLogStream.write(defaultHeader); + } + if (this.fileProperties.enableFatalLog) { + this.fatalLogStream = fs.createWriteStream( + Path.join(this.fileProperties.logFolderPath, 'fatal-crash', `fatal-latest.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' }, + ); + if (this.fileProperties.generateHTMLLog) this.fatalLogStream.write(defaultHeader); } + + // handles process exists to properly close the streams + process.on('exit', this.closeFileStreams.bind(this, 'Process exited', undefined)); } else { this.fileProperties.enableLatestLog = false; this.fileProperties.enableDebugLog = false; @@ -92,22 +92,27 @@ export class Logger { this.fileProperties.generateHTMLLog = false; this.fileProperties.compressLogFilesAfterNewExecution = false; } + } - // color for text or colored background - this.BaseColors = { - [ELoggerLevel.INFO]: '#cc80ff', - [ELoggerLevel.WARN]: '#ff8a1c', - [ELoggerLevel.ERROR]: '#ff4a4a', - [ELoggerLevel.DEBUG]: '#555555', - }; + private closeFileStreams(closeStreamMessage?: string, customFatalMessage?: string): void { + if (this.latestFileStream) this.latestFileStream.end(closeStreamMessage?.toString()); + if (this.debugLogStream) this.debugLogStream.end(closeStreamMessage?.toString()); + if (this.errorLogStream) this.errorLogStream.end(closeStreamMessage?.toString()); + if (this.fatalLogStream) { + this.fatalLogStream?.end((customFatalMessage ?? closeStreamMessage)?.toString()); + // rename the file from fatal-latest.log to fatal-.log + fs.renameSync( + Path.resolve(this.fileProperties.logFolderPath, 'fatal-crash', `fatal-latest.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), + Path.resolve(this.fileProperties.logFolderPath, 'fatal-crash', `fatal-${this.getTime(true, true)}.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), + ); + } + } - // color for text on colored background - this.AlternateColors = { - [ELoggerLevel.INFO]: '#000000', - [ELoggerLevel.WARN]: '#000000', - [ELoggerLevel.ERROR]: '#000000', - [ELoggerLevel.DEBUG]: '#D4D4D4', - }; + private writeToAllStreams(message: string, customFatalLog?: string): void { + if (this.fileProperties.enableLatestLog) this.latestFileStream?.write(message); + if (this.fileProperties.enableDebugLog) this.debugLogStream?.write(message); + if (this.fileProperties.enableErrorLog) this.errorLogStream?.write(message); + if (this.fileProperties.enableFatalLog) this.fatalLogStream?.write(customFatalLog ?? message); } private compressLastSessionLogs(): void { @@ -116,9 +121,9 @@ export class Logger { const zip = new admZip(); var files = fs.readdirSync(this.fileProperties.logFolderPath); - const fatalCrashFiles = fs.readdirSync(Path.join(this.fileProperties.logFolderPath, 'fatal-crash')); + // const fatalCrashFiles = fs.readdirSync(Path.join(this.fileProperties.logFolderPath, 'fatal-crash')); const latestLogsFiles = fs.readdirSync(Path.join(this.fileProperties.logFolderPath, 'latestLogs')); - files = files.concat(fatalCrashFiles.map((file) => Path.join('fatal-crash', file))); + // files = files.concat(fatalCrashFiles.map((file) => Path.join('fatal-crash', file))); files = files.concat(latestLogsFiles.map((file) => Path.join('latestLogs', file))); files.forEach((file) => { if (file.endsWith('.log') || file.endsWith('.html')) { @@ -128,12 +133,7 @@ export class Logger { } }); - const date = new Date(Date.now()); - const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); - const month = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); - const year = date.getFullYear(); - - fs.writeFileSync(Path.resolve(this.fileProperties.logFolderPath, `logs-${year}-${month}-${day}-${this.getTime(true)}.zip`), zip.toBuffer()); + fs.writeFileSync(Path.resolve(this.fileProperties.logFolderPath, `logs-${this.getTime(true, true)}.zip`), zip.toBuffer()); } private getFormattedPrefix(): string { @@ -145,35 +145,46 @@ export class Logger { return this.prefix !== '' ? prefix : ''; } - private getTime(friendlySymbols?: boolean): string { + private getTime(fullDate?: boolean, friendlySymbols?: boolean): string { const time = new Date(Date.now()); + const day = time.getDate() < 10 ? '0' + time.getDate() : time.getDate(); + const month = time.getMonth() < 10 ? '0' + time.getMonth() : time.getMonth(); + const year = time.getFullYear(); const seconds = time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds(); const minutes = time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes(); const hours = time.getHours() < 10 ? '0' + time.getHours() : time.getHours(); - return `${friendlySymbols ? '' : '['}${hours}${friendlySymbols ? '-' : ':'}${minutes}${friendlySymbols ? '-' : ':'}${seconds}${friendlySymbols ? '' : ']'}`; + + // eslint-disable-next-line max-len + return `${friendlySymbols ? '' : '['}${fullDate ? day : ''}${fullDate ? (friendlySymbols ? '-' : ':') : ''}${fullDate ? month : ''}${fullDate ? (friendlySymbols ? '-' : ':') : ''}${fullDate ? year : ''}${fullDate ? (friendlySymbols ? 'T' : '-') : ''}${hours}${friendlySymbols ? '-' : ':'}${minutes}${friendlySymbols ? '-' : ':'}${seconds}${friendlySymbols ? '' : ']'}`; } - private generateMessagePrefix(level: ELoggerLevel): { colored: string; raw: string, txtColor: string } { - const fgColor = [this.BaseColors[level], this.AlternateColors[level]]; + private generateMessagePrefix(level: ELoggerLevel): { coloredMessagePrefix: string; rawMessagePrefix: string, textColor: string } { + const fgColor = [ELoggerLevelBaseColors[level], ELoggerLevelAlternateColors[level]]; var time = chalk.hex(fgColor[Number(this.coloredBackground)])(this.getTime() + ' '); var prefix = chalk.hex(fgColor[Number(this.coloredBackground)])(this.getFormattedPrefix() + ' '); - var levelText = chalk.hex(fgColor[Number(this.coloredBackground)])(ELoggerLevel[level].toUpperCase() + ':'); + var levelText = chalk.hex(fgColor[Number(this.coloredBackground)])(ELoggerLevelNames[level].toUpperCase() + ':'); if (this.coloredBackground) { - time = chalk.bgHex(this.BaseColors[level])(time); - prefix = chalk.bgHex(this.BaseColors[level])(prefix); - levelText = chalk.bgHex(this.BaseColors[level])(levelText); + time = chalk.bgHex(ELoggerLevelBaseColors[level])(time); + prefix = chalk.bgHex(ELoggerLevelBaseColors[level])(prefix); + levelText = chalk.bgHex(ELoggerLevelBaseColors[level])(levelText); } return { - colored: `${time}${prefix}${levelText}`, - raw: `${this.getTime()} [${this.prefix}] ${ELoggerLevel[level].toUpperCase()}:`, - txtColor: fgColor[Number(this.coloredBackground)], + coloredMessagePrefix: `${time}${prefix}${levelText}`, + rawMessagePrefix: `${this.getTime()} [${this.prefix}] ${ELoggerLevelNames[level].toUpperCase()}:`, + textColor: fgColor[Number(this.coloredBackground)], }; } log(text: string | number | Error, levelToLog?: ELoggerLevel, ...args: any): void { const level = levelToLog ?? this.defaultLevel; + var stackTrace = ''; + if (text instanceof Error) { + stackTrace = text.stack ?? ''; + text = text.toString(); + } + text = utils.format(text, ...args); if (level === ELoggerLevel.FATAL) return this.fatal(text, ...args); const consoleLevels = { [ELoggerLevel.INFO]: console.log, @@ -182,21 +193,45 @@ export class Logger { [ELoggerLevel.DEBUG]: console.debug, }; - const { colored, raw, txtColor } = this.generateMessagePrefix(level); + const { coloredMessagePrefix, rawMessagePrefix, textColor } = this.generateMessagePrefix(level); - const msg = (this.coloredBackground && this.allLineColored) - ? chalk.bgHex(this.BaseColors[level])(chalk.hex(this.AlternateColors[level])(' ' + text)) - : (this.allLineColored ? chalk.hex(this.BaseColors[level])(' ' + text) : ' ' + text) + const messageToConsole = (this.coloredBackground && this.allLineColored) + ? chalk.bgHex(ELoggerLevelBaseColors[level])(chalk.hex(ELoggerLevelAlternateColors[level])(' ' + text)) + : (this.allLineColored ? chalk.hex(ELoggerLevelBaseColors[level])(' ' + text) : ' ' + text) ; - consoleLevels[level](colored + msg, ...args); + if ((this.debugActive && level === ELoggerLevel.DEBUG) || (level !== ELoggerLevel.DEBUG)) { + consoleLevels[level](coloredMessagePrefix + messageToConsole, ...args); + } + + // escapes the text to a be secure to be used in html + const escapedText = escape(text.toString()); + // escapes the stack trace and converts tabs to spaces and spaces to   to be used in html + const escapedStackTrace = '' + escape(stackTrace).replace(/\t/g, '    ').replace(/ /g, ' ').split('\n').join('') + ''; // eslint-disable-next-line max-len - const textSpan = this.allLineColored ? `${text}` : `${text}`; + const textSpanElement = this.allLineColored ? `${escapedText}` : `${escapedText}`; + // eslint-disable-next-line max-len + const stackTraceSpanElement = this.allLineColored ? `${escapedStackTrace}` : `${escapedStackTrace}`; // eslint-disable-next-line max-len - const prefixSpan = `${raw} ${textSpan}\n`; + const parentSpanElement = `${rawMessagePrefix} ${textSpanElement}\n`; + // eslint-disable-next-line max-len + const parentStackTraceSpanElement = `${rawMessagePrefix} ${stackTraceSpanElement}\n`; + if (this.fileProperties.enableDebugLog) { - this.debugLogStream?.write(this.fileProperties.generateHTMLLog ? prefixSpan : (raw + ' ' + text + '\n')); + this.debugLogStream?.write(this.fileProperties.generateHTMLLog ? parentSpanElement : (rawMessagePrefix + ' ' + text + '\n')); + } + if (this.fileProperties.enableErrorLog && level === ELoggerLevel.ERROR) { + // eslint-disable-next-line max-len + this.errorLogStream?.write(this.fileProperties.generateHTMLLog ? (stackTrace ? parentStackTraceSpanElement : parentSpanElement) : (rawMessagePrefix + ' ' + (stackTrace ?? text) + '\n')); + } + if (this.fileProperties.enableLatestLog && level !== ELoggerLevel.DEBUG) { + this.latestFileStream?.write(this.fileProperties.generateHTMLLog ? parentSpanElement : (rawMessagePrefix + ' ' + text + '\n')); + } + if (this.fileProperties.enableFatalLog && level !== ELoggerLevel.DEBUG) { + // write all logs to the fatal log file (including stack traces from non fatal logs) EXCEPT debug logs + // eslint-disable-next-line max-len + this.fatalLogStream?.write(this.fileProperties.generateHTMLLog ? (stackTrace ? parentStackTraceSpanElement : parentSpanElement) : (rawMessagePrefix + ' ' + (stackTrace ?? text) + '\n')); } } @@ -212,33 +247,64 @@ export class Logger { this.log(text, ELoggerLevel.ERROR, ...args); } + debug(text: string | number | Error, ...args: any): void { + this.log(text, ELoggerLevel.DEBUG, ...args); + } + fatal(text: string | number | Error, ...args: any): void { var message = text.toString(); + var stack: string[] | undefined = []; + var fullString = text.toString(); if (text instanceof Error) { // create stacktrace - const stack = text.stack?.split('\n'); + stack = text.stack?.split('\n'); if (stack) { - message = stack[0]; + fullString = stack.join('\n'); + message = stack.shift() ?? ''; } } + message = utils.format(message, ...args); + const time = this.getTime(); const prefix = this.getFormattedPrefix(); - const levelMsg = text.toString().startsWith('Error') ? 'FATAL ' : 'FATAL ERROR: '; + const levelMsg = text.toString().startsWith('Error') ? ELoggerLevelNames[3] : `${ELoggerLevelNames[3]} ${ELoggerLevelNames[2]}:`; - message = `${time} ${prefix} ${levelMsg} ${message.toString()}`; + message = `${time} ${prefix} ${levelMsg} ${message.toString()}${stack ? '\n' + stack.join('\n') : ''}`; const msg = chalk.bgWhite(chalk.redBright(message)); + var escapedFullText = escape(fullString); + const escapedText = escape(text.toString()); + + // convert tabs to html + escapedFullText = escapedFullText.replace(/\t/g, '    '); + escapedFullText = escapedFullText.replace(/ /g, ' '); + + const splitFullEscapedText = escapedFullText.split('\n'); + const htmlFullText = '' + splitFullEscapedText.join('') + ''; + + const textSpan = `${escapedText}`; + const fullSpan = `${htmlFullText}`; + // eslint-disable-next-line max-len + const prefixSpan = `${time} [${this.prefix}] ${levelMsg} ${textSpan}\n`; + // eslint-disable-next-line max-len + const fullPrefixSpan = `${time} [${this.prefix}] ${levelMsg} ${fullSpan}\n`; + + // eslint-disable-next-line max-len + const finalMessage = (this.fileProperties.generateHTMLLog ? prefixSpan : (time + ' [' + this.prefix + '] ' + levelMsg + ' ' + text + '\n')) + 'Please check the fatal log file for more details.\n'; + const finalFatalMessage = this.fileProperties.generateHTMLLog ? fullPrefixSpan : (time + ' [' + this.prefix + '] ' + levelMsg + ' ' + fullString + '\n'); + + if (this.disableFatalCrash) { + this.writeToAllStreams(finalMessage, finalFatalMessage); + } else { + this.closeFileStreams(finalMessage, finalFatalMessage); + } + console.error(msg, ...args); if (!this.disableFatalCrash) { process.exit(5); } } - - debug(text: string | number | Error, ...args: any): void { - if (!this.debugActive) return; - this.log(text, ELoggerLevel.DEBUG, ...args); - } } diff --git a/src/tests/tester.js b/src/tests/tester.js index 03ccaf8..1dcf61d 100644 --- a/src/tests/tester.js +++ b/src/tests/tester.js @@ -8,18 +8,21 @@ const log = new logger.Logger({ disableFatalCrash: true, fileProperties: { enable: true, - logFolderPath: './logs1', + logFolderPath: './logs', enableLatestLog: true, enableDebugLog: true, enableErrorLog: true, enableFatalLog: true, - generateHTMLLog: false, + generateHTMLLog: true, compressLogFilesAfterNewExecution: true, }, }); -log.debug('Hello world!'); -log.info('Hello world!'); -log.warn('Hello world!'); -log.error('Hello world!'); -log.fatal('Hello world!'); +const ERROR = new Error('Example error'); + +log.debug(ERROR); +log.info(['Hello world!', 'This is a test!']); +log.warn('

Hello world!

'); +log.error({ message: 'Hello world!', code: 500 }); +log.fatal(ERROR); +log.debug('This happens after a fatal error!');