Add new LogManager module

This commit is contained in:
Jesse Wierzbinski 2024-03-08 15:25:17 -10:00
parent 177f573792
commit 3b75f5f0a5
No known key found for this signature in database
5 changed files with 165 additions and 1 deletions

4
.gitignore vendored
View file

@ -170,4 +170,6 @@ dist
config/config.toml config/config.toml
config/config.internal.toml config/config.internal.toml
uploads/ uploads/
pages/dist pages/dist
log.txt
*.log

View file

@ -2,6 +2,7 @@
* @file configmanager.ts * @file configmanager.ts
* @summary ConfigManager system to retrieve and modify system configuration * @summary ConfigManager system to retrieve and modify system configuration
* @description Can read from a hand-written file, config.toml, or from a machine-saved file, config.internal.toml * @description Can read from a hand-written file, config.toml, or from a machine-saved file, config.internal.toml
* @deprecated Use the new ConfigManager class instead
* Fuses both and provides a way to retrieve individual values * Fuses both and provides a way to retrieve individual values
*/ */

View file

@ -0,0 +1,61 @@
import type { BunFile } from "bun";
import { appendFile } from "fs/promises";
export enum LogLevel {
DEBUG = "debug",
INFO = "info",
WARNING = "warning",
ERROR = "error",
CRITICAL = "critical",
}
/**
* Class for handling logging to disk or to stdout
* @param output BunFile of output (can be a normal file or something like Bun.stdout)
*/
export class LogManager {
constructor(private output: BunFile) {
void this.write(
`--- INIT LogManager at ${new Date().toISOString()} --`
);
}
/**
* Logs a message to the output
* @param level Importance of the log
* @param entity Emitter of the log
* @param message Message to log
* @param showTimestamp Whether to show the timestamp in the log
*/
async log(
level: LogLevel,
entity: string,
message: string,
showTimestamp = true
) {
await this.write(
`${showTimestamp ? new Date().toISOString() + " " : ""}[${level.toUpperCase()}] ${entity}: ${message}`
);
}
private async write(text: string) {
if (this.output == Bun.stdout) {
await Bun.write(Bun.stdout, text + "\n");
} else {
if (!this.output.name) {
throw new Error(`Output file doesnt exist (and isnt stdout)`);
}
await appendFile(this.output.name, text + "\n");
}
}
/**
* Logs an error to the output, wrapper for log
* @param level Importance of the log
* @param entity Emitter of the log
* @param error Error to log
*/
async logError(level: LogLevel, entity: string, error: Error) {
await this.log(level, entity, error.message);
}
}

View file

@ -0,0 +1,6 @@
{
"name": "log-manager",
"version": "0.0.0",
"main": "index.ts",
"dependencies": { }
}

View file

@ -0,0 +1,94 @@
// FILEPATH: /home/jessew/Dev/lysand/packages/log-manager/log-manager.test.ts
import { LogManager, LogLevel } from "../index";
import type fs from "fs/promises";
import {
describe,
it,
beforeEach,
expect,
jest,
mock,
type Mock,
test,
} from "bun:test";
import type { BunFile } from "bun";
describe("LogManager", () => {
let logManager: LogManager;
let mockOutput: BunFile;
let mockAppend: Mock<typeof fs.appendFile>;
beforeEach(async () => {
mockOutput = Bun.file("test.log");
mockAppend = jest.fn();
await mock.module("fs/promises", () => ({
appendFile: mockAppend,
}));
logManager = new LogManager(mockOutput);
});
it("should initialize and write init log", () => {
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("--- INIT LogManager at")
);
});
it("should log message with timestamp", async () => {
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("[INFO] TestEntity: Test message")
);
});
it("should log message without timestamp", async () => {
await logManager.log(
LogLevel.INFO,
"TestEntity",
"Test message",
false
);
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
"[INFO] TestEntity: Test message\n"
);
});
test.skip("should write to stdout", async () => {
logManager = new LogManager(Bun.stdout);
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
const writeMock = jest.fn();
await mock.module("Bun", () => ({
stdout: Bun.stdout,
write: writeMock,
}));
expect(writeMock).toHaveBeenCalledWith(
Bun.stdout,
expect.stringContaining("[INFO] TestEntity: Test message")
);
});
it("should throw error if output file does not exist", () => {
mockAppend.mockImplementationOnce(() => {
return Promise.reject(
new Error("Output file doesnt exist (and isnt stdout)")
);
});
expect(
logManager.log(LogLevel.INFO, "TestEntity", "Test message")
).rejects.toThrow(Error);
});
it("should log error message", async () => {
const error = new Error("Test error");
await logManager.logError(LogLevel.ERROR, "TestEntity", error);
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("[ERROR] TestEntity: Test error")
);
});
});