RenaiApp/src/main/modules/logger/logger.ts

107 lines
3.1 KiB
TypeScript

import path from 'path';
import { Readable } from 'stream';
import * as fs from 'fs-extra';
import { injectable } from 'inversify';
import { appPath } from '../../core/app-path';
import { isDev } from '../../core/env';
import { LogLevel } from './log-level';
const loggingDir = path.resolve(appPath, 'logs');
const logFile = path.resolve(loggingDir, 'default.log');
const exceptionLogFile = path.resolve(loggingDir, 'exception.log');
const maxLogSize = 50000;
/**
* A logger not using winston to log to files in the appPath.
*/
@injectable()
export class Logger implements LoggerInterface {
public constructor() {
fs.createFileSync(logFile);
fs.createFileSync(exceptionLogFile);
}
private static writeStream(stream: NodeJS.ReadableStream, filePath: string): Promise<void> {
return new Promise((resolve) => {
let buffer = fs.readFileSync(filePath, {
flag: 'r',
});
stream.on('data', (chunk) => {
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
});
stream.on('end', () => {
const diff = buffer.byteLength - maxLogSize;
if (diff > 0) {
buffer = buffer.slice(diff);
const firstLineBreakIndex = buffer.findIndex((value) => String.fromCharCode(value) === '\n');
buffer = buffer.slice(firstLineBreakIndex + 1);
}
fs.writeFileSync(filePath, buffer);
resolve();
});
});
}
private static writeLine(line: string): Promise<void> {
return Logger.writeStream(Readable.from(`${line}\n`), logFile);
}
private static formatLine(level: LogLevel, message: string): string {
return `[${new Date().toISOString()}] ${LogLevel[level]}: ${message}`;
}
public getLogFile(): string {
return logFile;
}
public getExceptionsLogFile(): string {
return exceptionLogFile;
}
public exception(error: Error): Promise<void> {
return Logger.writeStream(
Readable.from(
`[${new Date().toISOString()}] ${(error as NodeJS.ErrnoException).code ?? 'Error'}: ${error.message}
${error.stack ?? 'no stack trace'}\n`
),
exceptionLogFile
);
}
public log(level: LogLevel, message: string): Promise<void> {
if (!isDev() && level === LogLevel.debug) {
return Promise.resolve();
}
return Logger.writeLine(Logger.formatLine(level, message)).catch((error) => {
// eslint-disable-next-line no-console -- I don't want logging to be a source of a fatal error, but i want to log it somewhere
console.error(error);
});
}
public fatal(message: string): Promise<void> {
return this.log(LogLevel.fatal, message);
}
public error(message: string): Promise<void> {
return this.log(LogLevel.error, message);
}
public warning(message: string): Promise<void> {
return this.log(LogLevel.warning, message);
}
public notice(message: string): Promise<void> {
return this.log(LogLevel.notice, message);
}
public info(message: string): Promise<void> {
return this.log(LogLevel.info, message);
}
public debug(message: string): Promise<void> {
return this.log(LogLevel.debug, message);
}
}