Skip to content

Commit

Permalink
Merge pull request #6 from JayPaulinCodes/feature/addFileClosing
Browse files Browse the repository at this point in the history
feat(logger/file): Added ability to close the logger stream
  • Loading branch information
JayPaulinCodes authored Mar 23, 2024
2 parents 0cf6827 + 91a2fa4 commit 34748f1
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 12 deletions.
32 changes: 30 additions & 2 deletions examples/example-1.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
const logger = require("../lib/index.js");

const LOG = new logger.Logger();
(async () => {
const sleep = (ms) => new Promise(resolve => { setTimeout(resolve, ms); });
const LOG = new logger.Logger({
output: {
file: {
enabled: true
}
}
});

LOG.log("Test");
await sleep (5);
LOG.log("Test 1");

let resC;
do {
const [ x, y ] = LOG.close();
console.log("CLOSE", x, y);
resC = x;
} while (!resC);

LOG.log("Test 2");

let resO;
do {
const [ x, y ] = LOG.open();
console.log("OPEN", x, y);
resO = x;
} while (!resO);

LOG.log("Test 3");
})()
5 changes: 5 additions & 0 deletions examples/example-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const logger = require("../lib/index.js");

const LOG = new logger.Logger();

LOG.log("Test 1");
27 changes: 27 additions & 0 deletions examples/example-3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const logger = require("../lib/index.js");

const LOG = new logger.Logger({
output: {
file: {
enabled: true
}
}
});

LOG.log("Test 1");

setTimeout(() => {
LOG.close();
}, 50)

setTimeout(() => {
LOG.log("Test 2");
}, 150)

setTimeout(() => {
LOG.open();
}, 250)

setTimeout(() => {
LOG.log("Test 3");
}, 350)
65 changes: 59 additions & 6 deletions src/classes/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hostname } from "node:os";
import { WriteStream, mkdirSync, statSync, createWriteStream, readdirSync, unlinkSync } from "node:fs";
import { WriteStream, mkdirSync, statSync, createWriteStream, readdirSync, unlinkSync, existsSync } from "node:fs";
import dateFormat from "./DateFormat";
import { OptionsBuilder } from "./OptionsBuilder";
import { DirectoryCreationError } from "./errors/DirectoryCreationError";
Expand All @@ -8,13 +8,17 @@ import { LogLevelValues } from "../enums/LogLevelValues";
import { LoggerOptions } from "../interfaces/LoggerOptions";
import { PartialLoggerOptions } from "../interfaces/partials/PartialLoggerOptions";
import { LogLevels } from "../types/LogLevels";
import { format } from "node:util";
import path from "node:path";

export class Logger {
private readonly _options: LoggerOptions;
private readonly outputFormatter: (data: { [index: string]: any }) => string;
private writeStream: WriteStream | null = null;
private tempFileBuffer: string[] = [];
private initComplete: boolean = false;
private closing: boolean = false;
private opening: boolean = false;
private fileSwitchTimeout?: NodeJS.Timeout;

constructor(options: PartialLoggerOptions = {}) {
Expand All @@ -26,7 +30,8 @@ export class Logger {
? LogFormatting[this.options.output.formatting].bind(this) : this.options.output.formatting.bind(this);

// Run out init function
this.init();
const [ openSucceeded, msg ] = this.open();
if (!openSucceeded) throw new Error(`Init Error: ${msg}`);
}

public get options(): LoggerOptions {
Expand All @@ -43,7 +48,8 @@ export class Logger {

private init(): void {
// Make sure we haven't already ran the init function
if (this.initComplete) return;
if (this.initComplete || this.opening) return;
this.opening = true;

// Create the log file and schedule it's swap if needed
if (this.options.output.file.enabled) {
Expand Down Expand Up @@ -82,9 +88,16 @@ export class Logger {

private setUpLogFile() {
// Setup the new file
this.createDirIfNeeded(this.options.output.file.outputDirectory);
const fileName = this.getFileNameFromDate();
const filePath = this.options.output.file.outputDirectory + fileName;
const trueFileDir = path.normalize(this.options.output.file.outputDirectory);
this.createDirIfNeeded(trueFileDir);
let fileName: string;
let filePath: string;
let iterations = 0;
do {
fileName = iterations > 0 ? format("%s (%s).log", this.getFileNameFromDate(), iterations) : format("%s.log", this.getFileNameFromDate());
filePath = trueFileDir + fileName;
iterations++;
} while (existsSync(format("%s\\%s", process.cwd(), filePath)));
const oldStream = this.writeStream;
const newStream = createWriteStream(filePath, { flags: "wx", encoding: "utf8" });
this.writeStream = null;
Expand All @@ -106,6 +119,8 @@ export class Logger {
oldStream.write(`--- Log file closed as of ${dateFormat(new Date(), "yyyy-mm-dd HH:MM:ss.l Z", this.options.output.useZuluTime)} ---`);
oldStream.end();
}

this.opening = false;
});

return;
Expand Down Expand Up @@ -145,6 +160,9 @@ export class Logger {
}

private print(level: LogLevels, message?: string, error?: Error): void {
// If we are in the process of closing or opening the file & logger don't log
if (this.closing || this.opening) return;

// If we have disabled bot console and file logging stop
if (!this.options.output.console.enabled && this.options.output.file.enabled) return;

Expand Down Expand Up @@ -176,6 +194,41 @@ export class Logger {
}
}

public close(): [boolean, string | null] {
if (this.opening) return [ false, "Cannot close while opening" ];
if (this.closing) return [ false, "Cannot close while already closing" ];
if (!this.initComplete) return [ false, "Cannot close already closed logger" ];

this.closing = true;
clearTimeout(this.fileSwitchTimeout);

const oldStream = this.writeStream;
const oldBuffer = this.tempFileBuffer;
this.writeStream = null;
this.tempFileBuffer = [];

if (oldStream !== null) {
// Write the temp storage to the stream
if (oldBuffer.length > 0) oldBuffer.forEach(elem => oldStream.write(elem));

oldStream.write(`--- Log file closed as of ${dateFormat(new Date(), "yyyy-mm-dd HH:MM:ss.l Z", this.options.output.useZuluTime)} ---`);
oldStream.end();
}

this.initComplete = false;
this.closing = false;

return [ true, null ];
}

public open(): [boolean, string | null] {
if (this.closing) return [ false, "Cannot open while closing" ];
if (this.opening) return [ false, "Cannot open while already opening" ];

this.init();
return [ true, null ];
}

public debug(message: string): void {
this.print("debug", message);
}
Expand Down
4 changes: 2 additions & 2 deletions src/classes/OptionsBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export class OptionsBuilder {
},
file: {
enabled: false,
outputDirectory: "./logs/",
outputFileName: "yyyy-mm-dd'T'HH-MM-ss'.log'"
outputDirectory: ".\\logs\\",
outputFileName: "yyyy-mm-dd'T'HH-MM-ss"
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/FileOutputOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export interface FileOutputOptions {

/**
* The directory to create the log files in
* Default: './logs/'
* Default: '.\\logs\\'
*/
outputDirectory: string;

/**
* The format for the name of the log files
* Supports DateFormat variables
* Default: 'yyyy-mm-dd'T'hh-MM-ss.log'
* Default: 'yyyy-mm-dd'T'hh-MM-ss'
*/
outputFileName: string;
}

0 comments on commit 34748f1

Please sign in to comment.