From 3d72924c6850f1a5ab3ae1dcc5cb19672f35395a Mon Sep 17 00:00:00 2001
From: Space_Interprise <44732812+emanuelfranklyn@users.noreply.github.com>
Date: Wed, 19 Jul 2023 21:55:17 -0300
Subject: [PATCH 1/4] (fix): duplicated logs and typescript types
---
CHANGELOG.md | 5 +++++
package-lock.json | 4 ++--
package.json | 2 +-
src/main/logger.ts | 16 ++++++++--------
src/tests/tester.js | 1 +
5 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5adc5b..a119442 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# V1.1.1
+ - [FIXED] Fixed a bug that caused logs that passed arguments to log the same thing twice
+
+ - [FIXED] Fixed typescript types for the `Logger` class
+
# V1.1
- Added support for logging to a file
diff --git a/package-lock.json b/package-lock.json
index 6d5c7d3..78dda03 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@promisepending/logger.js",
- "version": "1.1.0",
+ "version": "1.1.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@promisepending/logger.js",
- "version": "1.1.0",
+ "version": "1.1.1",
"license": "MIT",
"dependencies": {
"adm-zip": "^0.5.10",
diff --git a/package.json b/package.json
index 0cad410..228466b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@promisepending/logger.js",
- "version": "1.1.0",
+ "version": "1.1.1",
"description": "A better logger",
"main": "build/index.js",
"types": "src/main/index.ts",
diff --git a/src/main/logger.ts b/src/main/logger.ts
index 90a6afb..af9b294 100644
--- a/src/main/logger.ts
+++ b/src/main/logger.ts
@@ -177,7 +177,7 @@ export class Logger {
};
}
- log(text: string | number | Error, levelToLog?: ELoggerLevel, ...args: any): void {
+ log(text: any, levelToLog?: ELoggerLevel, ...args: any): void {
const level = levelToLog ?? this.defaultLevel;
var stackTrace = '';
if (text instanceof Error) {
@@ -201,7 +201,7 @@ export class Logger {
;
if ((this.debugActive && level === ELoggerLevel.DEBUG) || (level !== ELoggerLevel.DEBUG)) {
- consoleLevels[level](coloredMessagePrefix + messageToConsole, ...args);
+ consoleLevels[level](coloredMessagePrefix + messageToConsole);
}
// escapes the text to a be secure to be used in html
@@ -235,23 +235,23 @@ export class Logger {
}
}
- info(text: string | number | Error, ...args: any): void {
+ info(text: any, ...args: any): void {
this.log(text, ELoggerLevel.INFO, ...args);
}
- warn(text: string | number | Error, ...args: any): void {
+ warn(text: any, ...args: any): void {
this.log(text, ELoggerLevel.WARN, ...args);
}
- error(text: string | number | Error, ...args: any): void {
+ error(text: any, ...args: any): void {
this.log(text, ELoggerLevel.ERROR, ...args);
}
- debug(text: string | number | Error, ...args: any): void {
+ debug(text: any, ...args: any): void {
this.log(text, ELoggerLevel.DEBUG, ...args);
}
- fatal(text: string | number | Error, ...args: any): void {
+ fatal(text: any, ...args: any): void {
var message = text.toString();
var stack: string[] | undefined = [];
var fullString = text.toString();
@@ -301,7 +301,7 @@ export class Logger {
this.closeFileStreams(finalMessage, finalFatalMessage);
}
- console.error(msg, ...args);
+ console.error(msg);
if (!this.disableFatalCrash) {
process.exit(5);
diff --git a/src/tests/tester.js b/src/tests/tester.js
index 1dcf61d..0644ada 100644
--- a/src/tests/tester.js
+++ b/src/tests/tester.js
@@ -24,5 +24,6 @@ log.debug(ERROR);
log.info(['Hello world!', 'This is a test!']);
log.warn('
Hello world!
');
log.error({ message: 'Hello world!', code: 500 });
+log.error('This is a string', { message: 'Hello world!', code: 500 });
log.fatal(ERROR);
log.debug('This happens after a fatal error!');
From 1fec8079ab34ccb265f89d998416022ae2d9426b Mon Sep 17 00:00:00 2001
From: Space_Interprise <44732812+emanuelfranklyn@users.noreply.github.com>
Date: Thu, 20 Jul 2023 00:04:11 -0300
Subject: [PATCH 2/4] (fix): Fatal and zip
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com>
---
CHANGELOG.md | 30 +++++++++++++------
README.md | 3 ++
package.json | 3 +-
src/main/logger.ts | 72 +++++++++++++++++++--------------------------
src/tests/tester.js | 46 +++++++++++++----------------
5 files changed, 78 insertions(+), 76 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a119442..c3e6db0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,17 +1,27 @@
-# V1.1.1
+# v1.1.1: Saving Everything
- [FIXED] Fixed a bug that caused logs that passed arguments to log the same thing twice
- [FIXED] Fixed typescript types for the `Logger` class
-# V1.1
- - Added support for logging to a file
+ - [FIXED] Fixed an issue that caused having two logger instances loggin to the same folder to cause a crash when the second instance tried to close the file stream
+
+ - [FIXED] Zip files timestamp now reflects the last modified time of the latest log file instead of the time of the zip creation
+
+ - [UPDATE] Fatal logs now are saved per fatal crash instead of all fatal crashes of the same execution being saved to the same file
+
+ - [UPDATE] Fatal logs now have a 4 character random code at the start of the file name to prevent overwriting of files
+
+ - [UPDATE] Zip files now have a 4 character random code at the start of the file name to prevent overwriting of files
+
+# v1.1.0: Saving Everything
+ - [NEW] 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`)
@@ -23,15 +33,17 @@
- 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
+# v1.0.0: Logging Everything
+
+- [NEW] Initial release of logger
-- Initial release of logger
- Supports Warning, Error, Info, Debug, and Fatal logging levels
+
- Supports colored output if specified
diff --git a/README.md b/README.md
index 1bb7259..44f5572 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,9 @@
+
+
+
#
diff --git a/package.json b/package.json
index 228466b..64a57d3 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"scripts": {
"build": "npx tsc -p .",
"pdeploy": "node scripts/prepareDeploy.js",
- "test": "node src/tests/tester.js"
+ "test": "node src/tests/tester.js",
+ "pretest": "npm run build"
},
"repository": {
"type": "git",
diff --git a/src/main/logger.ts b/src/main/logger.ts
index af9b294..d3b6d37 100644
--- a/src/main/logger.ts
+++ b/src/main/logger.ts
@@ -17,9 +17,9 @@ export class Logger {
private latestFileStream?: fs.WriteStream;
private debugLogStream?: fs.WriteStream;
private errorLogStream?: fs.WriteStream;
- private fatalLogStream?: fs.WriteStream;
private htmlBackgroundColor: string;
private htmlTextColor: string;
+ private defaultHeader = '';
constructor({ prefix, debug, defaultLevel, coloredBackground, disableFatalCrash, allLineColored, fileProperties }: ILoggerOptions) {
this.prefix = prefix ?? '';
@@ -55,35 +55,32 @@ 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`;
+ this.defaultHeader = `\n`;
if (this.fileProperties.enableLatestLog) {
this.latestFileStream = fs.createWriteStream(
Path.join(this.fileProperties.logFolderPath, `latest.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' },
);
- if (this.fileProperties.generateHTMLLog) this.latestFileStream.write(defaultHeader);
+ if (this.fileProperties.generateHTMLLog) this.latestFileStream.write(this.defaultHeader);
}
if (this.fileProperties.enableDebugLog) {
this.debugLogStream = fs.createWriteStream(
Path.join(this.fileProperties.logFolderPath, 'latestLogs', `debug.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' },
);
- if (this.fileProperties.generateHTMLLog) this.debugLogStream.write(defaultHeader);
+ if (this.fileProperties.generateHTMLLog) this.debugLogStream.write(this.defaultHeader);
}
if (this.fileProperties.enableErrorLog) {
this.errorLogStream = fs.createWriteStream(
Path.join(this.fileProperties.logFolderPath, 'latestLogs', `error.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`), { flags: 'a' },
);
- 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);
+ if (this.fileProperties.generateHTMLLog) this.errorLogStream.write(this.defaultHeader);
}
// handles process exists to properly close the streams
- process.on('exit', this.closeFileStreams.bind(this, 'Process exited', undefined));
+ process.on('exit', (exitCode) => {
+ // eslint-disable-next-line max-len
+ this.closeFileStreams(`${this.fileProperties.generateHTMLLog ? '
\n' : '\n'}Process exited with code (${exitCode})${this.fileProperties.generateHTMLLog ? '\n
' : '\n'}`);
+ });
} else {
this.fileProperties.enableLatestLog = false;
this.fileProperties.enableDebugLog = false;
@@ -95,24 +92,26 @@ export class Logger {
}
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'}`),
- );
- }
+ this.writeToAllStreams(closeStreamMessage ?? '', customFatalMessage);
+ this.latestFileStream?.end();
+ this.debugLogStream?.end();
+ this.errorLogStream?.end();
}
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);
+ if (this.fileProperties.enableFatalLog && customFatalLog) {
+ // create a new stream for fatal log
+ // 4 random alphanumeric characters
+ const uniqueId = Math.random().toString(36).substring(2, 6);
+ const fatalLogStream = fs.createWriteStream(
+ Path.join(this.fileProperties.logFolderPath, 'fatal-crash', `fatal-${uniqueId}-${this.getTime(true, true)}.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`),
+ );
+ fatalLogStream.write(this.defaultHeader);
+ fatalLogStream.end(customFatalLog);
+ }
}
private compressLastSessionLogs(): void {
@@ -125,6 +124,11 @@ export class Logger {
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)));
+ // use fs.stat on latest.log/html to get its last modified date
+ const latestLogPath = Path.join(this.fileProperties.logFolderPath, `latest.${this.fileProperties.generateHTMLLog ? 'html' : 'log'}`);
+ const latestLogStats = fs.statSync(latestLogPath);
+ // get mtime and replace : with - to avoid windows file system errors
+ const latestLogDate = latestLogStats.mtime.toISOString().replace(/:/g, '-').split('.')[0];
files.forEach((file) => {
if (file.endsWith('.log') || file.endsWith('.html')) {
zip.addLocalFile(Path.join(this.fileProperties.logFolderPath, file));
@@ -132,8 +136,8 @@ export class Logger {
if (!file.startsWith('fatal')) fs.unlinkSync(Path.join(this.fileProperties.logFolderPath, file));
}
});
-
- fs.writeFileSync(Path.resolve(this.fileProperties.logFolderPath, `logs-${this.getTime(true, true)}.zip`), zip.toBuffer());
+ const uniqueId = Math.random().toString(36).substring(2, 6);
+ fs.writeFileSync(Path.resolve(this.fileProperties.logFolderPath, `logs-${uniqueId}-${latestLogDate}.zip`), zip.toBuffer());
}
private getFormattedPrefix(): string {
@@ -179,9 +183,7 @@ export class Logger {
log(text: any, 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);
@@ -206,33 +208,22 @@ export class Logger {
// 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 textSpanElement = this.allLineColored ? `${escapedText}` : `${escapedText}`;
// eslint-disable-next-line max-len
- const stackTraceSpanElement = this.allLineColored ? `${escapedStackTrace}` : `${escapedStackTrace}`;
- // eslint-disable-next-line max-len
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 ? 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'));
+ this.errorLogStream?.write(this.fileProperties.generateHTMLLog ? parentSpanElement : (rawMessagePrefix + ' ' + 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'));
- }
}
info(text: any, ...args: any): void {
@@ -256,7 +247,6 @@ export class Logger {
var stack: string[] | undefined = [];
var fullString = text.toString();
if (text instanceof Error) {
- // create stacktrace
stack = text.stack?.split('\n');
if (stack) {
fullString = stack.join('\n');
diff --git a/src/tests/tester.js b/src/tests/tester.js
index 0644ada..14da7f9 100644
--- a/src/tests/tester.js
+++ b/src/tests/tester.js
@@ -1,29 +1,25 @@
-const logger = require('../../build');
+const { Logger } = require('../../build');
-const log = new logger.Logger({
- prefix: 'TESTER',
- debug: true,
- coloredBackground: true,
- allLineColored: true,
- disableFatalCrash: true,
- fileProperties: {
- enable: true,
- logFolderPath: './logs',
- enableLatestLog: true,
- enableDebugLog: true,
- enableErrorLog: true,
- enableFatalLog: true,
- generateHTMLLog: true,
- compressLogFilesAfterNewExecution: true,
+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: true, // 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)
},
});
-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.error('This is a string', { message: 'Hello world!', code: 500 });
-log.fatal(ERROR);
-log.debug('This happens after a fatal error!');
+logger.info('This is an info message');
+logger.warn('This is a warning message');
+logger.error('This is an error message');
+logger.debug('This is a debug message');
+logger.fatal('This is a fatal message');
From e6bcc2f32e29726767e37eda64f5643123ae61fa Mon Sep 17 00:00:00 2001
From: Space_Interprise <44732812+emanuelfranklyn@users.noreply.github.com>
Date: Thu, 20 Jul 2023 00:11:22 -0300
Subject: [PATCH 3/4] (chore): Added readme example picture
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com>
---
.github/assets/LoggerExample.png | Bin 0 -> 25970 bytes
README.md | 8 ++++++--
src/tests/tester.js | 2 +-
3 files changed, 7 insertions(+), 3 deletions(-)
create mode 100644 .github/assets/LoggerExample.png
diff --git a/.github/assets/LoggerExample.png b/.github/assets/LoggerExample.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4b65be9f6562974bdd4cfc9249f0cb75c41ea7c
GIT binary patch
literal 25970
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
+
📝 License
@@ -122,7 +126,7 @@ logger.fatal('This is a fatal message');
-
+
LoboMetalurgico
@@ -131,7 +135,7 @@ logger.fatal('This is a fatal message');
|
-
+
SpaceFox
diff --git a/src/tests/tester.js b/src/tests/tester.js
index 14da7f9..cf11316 100644
--- a/src/tests/tester.js
+++ b/src/tests/tester.js
@@ -4,7 +4,7 @@ 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: true, // If true, the background of the lines will be colored instead of the text (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]
From 898e4464f57ec150257fbec30c8c621f2ada0569 Mon Sep 17 00:00:00 2001
From: Space_Interprise <44732812+emanuelfranklyn@users.noreply.github.com>
Date: Thu, 20 Jul 2023 00:13:20 -0300
Subject: [PATCH 4/4] (chore): Fixed changelog
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com>
---
CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3e6db0..5d54d45 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,13 @@
-# v1.1.1: Saving Everything
+# v1.1.1: Saving Everything (patch)
- [FIXED] Fixed a bug that caused logs that passed arguments to log the same thing twice
- [FIXED] Fixed typescript types for the `Logger` class
- - [FIXED] Fixed an issue that caused having two logger instances loggin to the same folder to cause a crash when the second instance tried to close the file stream
+ - [FIXED] Fixed an issue that caused having two logger instances logging to the same folder to cause a crash when the second instance tried to close the file stream
- [FIXED] Zip files timestamp now reflects the last modified time of the latest log file instead of the time of the zip creation
- - [UPDATE] Fatal logs now are saved per fatal crash instead of all fatal crashes of the same execution being saved to the same file
+ - [UPDATE] Fatal logs now are saved per fatal crash, instead of all fatal crashes of the same execution being saved to the same file
- [UPDATE] Fatal logs now have a 4 character random code at the start of the file name to prevent overwriting of files
|