Redesign File writing and some tweaks

This commit is contained in:
Fabian Stamm 2018-08-09 19:52:01 +02:00
parent 55344fb638
commit 29fee51b63
11 changed files with 418 additions and 254 deletions

52
out/index.d.ts vendored
View File

@ -1,27 +1,37 @@
/// <reference types="node" /> /// <reference types="node" />
import { EventEmitter } from "events"; import { EventEmitter } from "events";
export declare class Logging { export interface LoggingBaseOptions {
private static logFileLocation; logfile: string;
static stdout: boolean; errorfile: string;
private static fileStream; console_out: boolean;
private static errorStream;
private static fileSize;
private static errorSize;
private static writing;
private static queue;
static events: EventEmitter;
static config(logfolder: string, stdout: boolean): void;
static debug(...message: any[]): void;
static log(...message: any[]): void;
static warning(...message: any[]): void;
static logWithCustomColors(type: LoggingTypes, colors: string, ...message: any[]): void;
static error(error: Error | string): void;
static errorMessage(...message: any[]): void;
private static message(type, message, customColors?);
private static writeMessageToFile(message, error?);
private static checkQueue();
private static initializeFile();
} }
export declare class LoggingBase {
private config;
private writeLock;
private fileStream;
private errorStream;
private fileSize;
private errorSize;
private queue;
constructor(options?: Partial<LoggingBaseOptions>);
console_out: boolean;
waitForSetup(): Promise<void>;
private setup();
events: EventEmitter;
debug(...message: any[]): void;
log(...message: any[]): void;
warning(...message: any[]): void;
logWithCustomColors(type: LoggingTypes, colors: string, ...message: any[]): void;
error(error: Error | string): void;
errorMessage(...message: any[]): void;
private message(type, message, customColors?);
private writeMessageToFile(message, error?);
private checkQueue();
private writeToLogFile(data);
private writeToErrorFile(data);
private initializeFile(file, new_file?);
}
export declare const Logging: LoggingBase;
export default Logging; export default Logging;
export declare enum LoggingTypes { export declare enum LoggingTypes {
Log = 0, Log = 0,

View File

@ -4,6 +4,7 @@ const util = require("util");
const fs = require("fs"); const fs = require("fs");
const events_1 = require("events"); const events_1 = require("events");
const path = require("path"); const path = require("path");
const lock_1 = require("./lock");
const Reset = "\x1b[0m"; const Reset = "\x1b[0m";
const Bright = "\x1b[1m"; const Bright = "\x1b[1m";
const Dim = "\x1b[2m"; const Dim = "\x1b[2m";
@ -29,36 +30,71 @@ const BgCyan = "\x1b[46m";
const BgWhite = "\x1b[47m"; const BgWhite = "\x1b[47m";
const maxFileSize = 500000000; const maxFileSize = 500000000;
const OriginalErrorStackFunction = Error.prototype.prepareStackTrace; const OriginalErrorStackFunction = Error.prototype.prepareStackTrace;
class Logging { class LoggingBase {
static config(logfolder, stdout) { constructor(options) {
this.logFileLocation = logfolder; this.writeLock = new lock_1.default();
this.stdout = stdout; this.fileSize = 0;
this.errorSize = 0;
this.queue = new Array();
this.events = new events_1.EventEmitter();
if (!options)
options = {};
this.config = Object.assign({
console_out: true,
logfile: "./logs/all.log",
errorfile: "./logs/error.log"
}, options);
this.setup();
} }
static debug(...message) { get console_out() {
Logging.message(LoggingTypes.Debug, message); return this.config.console_out;
} }
static log(...message) { set console_out(value) {
Logging.message(LoggingTypes.Log, message); this.config.console_out = value;
} }
static warning(...message) { async waitForSetup() {
Logging.message(LoggingTypes.Warning, message); (await this.writeLock.getLock()).release();
} }
static logWithCustomColors(type, colors, ...message) { async setup() {
Logging.message(type, message, colors); let lock = await this.writeLock.getLock();
if (this.config.logfile) {
let f = await this.initializeFile(this.config.logfile, true);
this.fileStream = f.stream;
this.fileSize = f.size;
}
if (this.config.errorfile) {
let f = await this.initializeFile(this.config.errorfile, true);
this.errorStream = f.stream;
this.errorSize = f.size;
}
lock.release();
this.checkQueue();
} }
static error(error) { debug(...message) {
this.message(LoggingTypes.Debug, message);
}
log(...message) {
this.message(LoggingTypes.Log, message);
}
warning(...message) {
this.message(LoggingTypes.Warning, message);
}
logWithCustomColors(type, colors, ...message) {
this.message(type, message, colors);
}
error(error) {
if (typeof error === "string") { if (typeof error === "string") {
let e = new Error(); let e = new Error();
Logging.message(LoggingTypes.Error, [error, ":", e.stack]); this.message(LoggingTypes.Error, [error, ":", e.stack]);
} }
else { else {
Logging.message(LoggingTypes.Error, [error.name, ":", error.message, ":", error.stack]); this.message(LoggingTypes.Error, [error.name, ":", error.message, ":", error.stack]);
} }
} }
static errorMessage(...message) { errorMessage(...message) {
Logging.message(LoggingTypes.Error, message); this.message(LoggingTypes.Error, message);
} }
static async message(type, message, customColors) { async message(type, message, customColors) {
var consoleLogFormat = Reset; var consoleLogFormat = Reset;
if (!customColors) { if (!customColors) {
switch (type) { switch (type) {
@ -97,132 +133,122 @@ class Logging {
} }
let file = getCallerFile(); let file = getCallerFile();
let date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''); let date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
var m = `[${LoggingTypes[type]}][${file.file}:${file.line}][${date}]: ${mb}`; let prefix = `[${LoggingTypes[type]}][${file.file}:${file.line}][${date}]: `;
if (this.stdout) let message_lines = mb.split("\n").map(line => prefix + line);
console.log(consoleLogFormat + m + Reset); if (this.config.console_out)
message_lines.forEach(line => console.log(consoleLogFormat + line + Reset));
let m = message_lines.join("\n");
let index = m.indexOf("\x1b"); let index = m.indexOf("\x1b");
while (index >= 0) { while (index >= 0) {
m = m.substring(0, index) + m.substring(index + 5, m.length); m = m.substring(0, index) + m.substring(index + 5, m.length);
index = m.indexOf("\x1b"); index = m.indexOf("\x1b");
} }
m = m.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ""); m = m.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
if (this.logFileLocation) { this.writeMessageToFile(m, type === LoggingTypes.Error);
if ((!this.fileStream || !this.errorStream) && !this.writing) { this.events.emit("message", { type: type, message: mb });
Logging.initializeFile();
}
Logging.writeMessageToFile(m, type === LoggingTypes.Error);
}
Logging.events.emit("message", { type: type, message: mb });
} }
static writeMessageToFile(message, error) { writeMessageToFile(message, error) {
Logging.queue.push({ message: message.replace("\n", " "), error: error }); if (!this.writeLock.locked && !this.fileStream && !(error || this.errorStream))
Logging.checkQueue(); return;
this.queue.push({ message: message.replace("\n", " "), error: error });
this.checkQueue();
} }
static async checkQueue() { async checkQueue() {
try { try {
if (Logging.writing) if (this.writeLock.locked)
return; return;
if (Logging.queue.length <= 0) if (this.queue.length <= 0)
return; return;
Logging.writing = true; let lock = await this.writeLock.getLock();
var message = Logging.queue[0]; var message = this.queue.shift();
message.message += "\n"; message.message += "\n";
let data = new Buffer(message.message, "utf8"); let data = new Buffer(message.message, "utf8");
if (data.byteLength < maxFileSize && data.byteLength + Logging.fileSize > maxFileSize) { await this.writeToLogFile(data);
Logging.fileStream.close(); if (message.error)
if (await fsExists(this.logFileLocation + "all.log.old")) await this.writeToErrorFile(data);
await fsUnlink(this.logFileLocation + "all.log.old"); lock.release();
await fsMove(this.logFileLocation + "all.log", this.logFileLocation + "all.log.old"); if (this.queue.length > 0)
Logging.fileStream = fs.createWriteStream(this.logFileLocation + "all.log"); this.checkQueue();
Logging.fileSize = 0;
}
Logging.fileSize += data.byteLength;
Logging.fileStream.write(data, async () => {
if (message.error) {
if (data.byteLength < maxFileSize && data.byteLength + Logging.errorSize > maxFileSize) {
Logging.errorStream.close();
if (await fsExists(this.logFileLocation + "error.log.old"))
await fsUnlink(this.logFileLocation + "error.log.old");
await fsMove(this.logFileLocation + "error.log", this.logFileLocation + "error.log.old");
Logging.errorStream = fs.createWriteStream(this.logFileLocation + "error.log");
Logging.errorSize = 0;
}
Logging.errorSize += data.byteLength;
Logging.errorStream.write(data, () => {
Logging.queue.splice(Logging.queue.indexOf(message), 1);
Logging.writing = false;
Logging.checkQueue();
});
}
else {
Logging.queue.splice(Logging.queue.indexOf(message), 1);
Logging.writing = false;
Logging.checkQueue();
}
});
} }
catch (e) { catch (e) {
console.log(e); console.log(e);
} }
} }
static async initializeFile() { async writeToLogFile(data) {
if (this.fileStream && this.errorStream) if (data.byteLength < maxFileSize && this.fileSize + data.byteLength > maxFileSize) {
return; let f = await this.initializeFile(this.config.logfile, true);
if (!this.logFileLocation) this.fileStream = f.stream;
return; this.fileSize = f.size;
this.writing = true; }
this.fileSize += data.byteLength;
this.fileStream.write(data);
}
async writeToErrorFile(data) {
if (data.byteLength < maxFileSize && this.errorSize + data.byteLength > maxFileSize) {
let f = await this.initializeFile(this.config.errorfile, true);
this.errorStream = f.stream;
this.errorSize = f.size;
}
this.errorSize += data.byteLength;
this.errorStream.write(data);
}
async initializeFile(file, new_file = true) {
try { try {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
fs.exists(this.logFileLocation, (exists) => { const folder = path.dirname(file);
if (!exists) { if (folder)
fs.mkdir(this.logFileLocation, (err) => { fs.exists(folder, (exists) => {
if (err) { if (!exists) {
reject(err); fs.mkdir(folder, (err) => {
} if (err) {
else { reject(err);
resolve(); }
} else {
}); resolve();
} }
else });
resolve(); }
}); else
resolve();
});
}); });
if (await fsExists(this.logFileLocation + "all.log")) { let size = 0;
if (await fsExists(this.logFileLocation + "all.log.old")) if (await fsExists(file)) {
await fsUnlink(this.logFileLocation + "all.log.old"); let stats = await fsStat(file);
await fsMove(this.logFileLocation + "all.log", this.logFileLocation + "all.log.old"); if (new_file || stats.size > maxFileSize) {
} if (await fsExists(file + ".old"))
if (await fsExists(this.logFileLocation + "error.log")) { await fsUnlink(file + ".old");
let stats = await fsStat(this.logFileLocation + "error.log"); await fsMove(file, file + ".old");
if (stats.size > maxFileSize) {
if (await fsExists(this.logFileLocation + "error.log.old"))
await fsUnlink(this.logFileLocation + "error.log.old");
await fsMove(this.logFileLocation + "error.log", this.logFileLocation + "error.log.old");
} }
else { else {
this.errorSize = stats.size; size = stats.size;
} }
} }
this.fileStream = fs.createWriteStream(this.logFileLocation + "all.log", { flags: "a" }); return { stream: fs.createWriteStream(file, { flags: "a" }), size: size };
this.errorStream = fs.createWriteStream(this.logFileLocation + "error.log", { flags: "a" }); // if (await fsExists(this.logFileLocation + "error.log")) {
this.writing = false; // let stats = await fsStat(this.logFileLocation + "error.log")
this.checkQueue(); // if (stats.size > maxFileSize) {
// if (await fsExists(this.logFileLocation + "error.log.old"))
// await fsUnlink(this.logFileLocation + "error.log.old");
// await fsMove(this.logFileLocation + "error.log", this.logFileLocation + "error.log.old")
// } else {
// this.errorSize = stats.size;
// }
// }
// this.fileStream = fs.createWriteStream(this.logFileLocation + "all.log", { flags: "a" });
// this.errorStream = fs.createWriteStream(this.logFileLocation + "error.log", { flags: "a" });
// this.writing = false;
// this.checkQueue();
} }
catch (e) { catch (e) {
console.log(e); console.log(e);
} }
return { size: 0, stream: undefined };
} }
} }
Logging.logFileLocation = "./logs/"; exports.LoggingBase = LoggingBase;
Logging.stdout = true; exports.Logging = new LoggingBase();
Logging.fileSize = 0; exports.default = exports.Logging;
Logging.errorSize = 0;
Logging.writing = false;
Logging.queue = new Array();
Logging.events = new events_1.EventEmitter();
exports.Logging = Logging;
exports.default = Logging;
function fsUnlink(path) { function fsUnlink(path) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.unlink(path, (err) => { fs.unlink(path, (err) => {

File diff suppressed because one or more lines are too long

12
out/lock.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
export declare type Release = {
release: () => void;
};
export default class Lock {
private _locked;
readonly locked: boolean;
private toCome;
constructor();
getLock(): Promise<Release>;
private lock();
private release();
}

37
out/lock.js Normal file
View File

@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Lock {
constructor() {
this._locked = false;
this.toCome = [];
this.release = this.release.bind(this);
}
get locked() {
return this._locked;
}
async getLock() {
if (!this._locked)
return { release: this.lock() };
else {
return new Promise((resolve) => {
this.toCome.push(() => {
resolve({ release: this.lock() });
});
});
}
}
lock() {
this._locked = true;
return this.release;
}
async release() {
if (this.toCome.length > 0) {
this.toCome.shift()();
}
else {
this._locked = false;
}
}
}
exports.default = Lock;
//# sourceMappingURL=lock.js.map

1
out/lock.js.map Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":";;AACA;IAOG;QANQ,YAAO,GAAY,KAAK,CAAC;QAIzB,WAAM,GAAmB,EAAE,CAAC;QAGjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAPD,IAAI,MAAM;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACvB,CAAC;IAOD,KAAK,CAAC,OAAO;QACV,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;aAC9C;YACF,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;gBACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;oBACnB,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACrC,CAAC,CAAC,CAAA;YACL,CAAC,CAAC,CAAA;SACJ;IACJ,CAAC;IAEO,IAAI;QACT,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,OAAO;QAClB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;SACxB;aAAM;YACJ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;SACvB;IACJ,CAAC;CACH;AAlCD,uBAkCC"}

View File

@ -11,8 +11,10 @@ index_1.Logging.log("\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m TEST \x1b[
let err = new Error(); let err = new Error();
if (typeof err.stack !== "string") if (typeof err.stack !== "string")
console.log("Stacktrace invalid", err.stack); console.log("Stacktrace invalid", err.stack);
index_1.Logging.stdout = false; index_1.Logging.console_out = false;
for (let i = 0; i < 7000; i++) { index_1.Logging.waitForSetup().then(() => {
index_1.Logging.log(crypto_1.randomBytes(50000).toString("hex")); for (let i = 0; i < 7000; i++) {
} index_1.Logging.log(crypto_1.randomBytes(50000).toString("hex"));
}
});
//# sourceMappingURL=test.js.map //# sourceMappingURL=test.js.map

View File

@ -1 +1 @@
{"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":";;AAAA,mCAAkC;AAClC,mCAAqC;AAErC,eAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AACnB,eAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;AAC1C,eAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;AACvC,eAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;AAClC,eAAO,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAE/C,eAAO,CAAC,GAAG,CAAC,gFAAgF,CAAC,CAAA;AAE7F,IAAI,GAAG,GAAG,IAAI,KAAK,EAAE,CAAA;AACrB,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;IAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;AAE/E,eAAO,CAAC,MAAM,GAAG,KAAK,CAAC;AACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE;IAC5B,eAAO,CAAC,GAAG,CAAC,oBAAW,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;CACjD"} {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":";;AAAA,mCAAkC;AAClC,mCAAqC;AAErC,eAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AACnB,eAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;AAC1C,eAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;AACvC,eAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;AAClC,eAAO,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAE/C,eAAO,CAAC,GAAG,CAAC,gFAAgF,CAAC,CAAA;AAE7F,IAAI,GAAG,GAAG,IAAI,KAAK,EAAE,CAAA;AACrB,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;IAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;AAC/E,eAAO,CAAC,WAAW,GAAG,KAAK,CAAC;AAC5B,eAAO,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE;QAC5B,eAAO,CAAC,GAAG,CAAC,oBAAW,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;KACjD;AACJ,CAAC,CAAC,CAAC"}

View File

@ -2,6 +2,7 @@ import * as util from "util";
import * as fs from "fs"; import * as fs from "fs";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import * as path from "path"; import * as path from "path";
import Lock from "./lock";
const Reset = "\x1b[0m" const Reset = "\x1b[0m"
const Bright = "\x1b[1m" const Bright = "\x1b[1m"
@ -33,55 +34,94 @@ const maxFileSize = 500000000;
const OriginalErrorStackFunction = (<any>Error.prototype).prepareStackTrace const OriginalErrorStackFunction = (<any>Error.prototype).prepareStackTrace
export class Logging { export interface LoggingBaseOptions {
private static logFileLocation: string = "./logs/"; logfile: string;
public static stdout: boolean = true; errorfile: string;
console_out: boolean;
}
private static fileStream: fs.WriteStream; export class LoggingBase {
private static errorStream: fs.WriteStream; private config: LoggingBaseOptions;
private static fileSize: number = 0; private writeLock = new Lock();
private static errorSize: number = 0;
private static writing = false; private fileStream: fs.WriteStream;
private static queue = new Array<{ message: string, error: boolean }>(); private errorStream: fs.WriteStream;
private fileSize: number = 0;
private errorSize: number = 0;
static events: EventEmitter = new EventEmitter(); private queue = new Array<{ message: string, error: boolean }>();
static config(logfolder: string, stdout: boolean) { constructor(options?: Partial<LoggingBaseOptions>) {
this.logFileLocation = logfolder; if (!options) options = {};
this.stdout = stdout; this.config = Object.assign(<LoggingBaseOptions>{
console_out: true,
logfile: "./logs/all.log",
errorfile: "./logs/error.log"
}, options);
this.setup();
} }
static debug(...message: any[]) { get console_out() {
Logging.message(LoggingTypes.Debug, message); return this.config.console_out;
} }
static log(...message: any[]) { set console_out(value) {
Logging.message(LoggingTypes.Log, message); this.config.console_out = value;
} }
static warning(...message: any[]) { public async waitForSetup() {
Logging.message(LoggingTypes.Warning, message); (await this.writeLock.getLock()).release();
} }
static logWithCustomColors(type: LoggingTypes, colors: string, ...message: any[]) { private async setup() {
Logging.message(type, message, colors); let lock = await this.writeLock.getLock();
if (this.config.logfile) {
let f = await this.initializeFile(this.config.logfile, true);
this.fileStream = f.stream;
this.fileSize = f.size;
}
if (this.config.errorfile) {
let f = await this.initializeFile(this.config.errorfile, true);
this.errorStream = f.stream;
this.errorSize = f.size;
}
lock.release();
this.checkQueue();
} }
static error(error: Error | string) { events: EventEmitter = new EventEmitter();
debug(...message: any[]) {
this.message(LoggingTypes.Debug, message);
}
log(...message: any[]) {
this.message(LoggingTypes.Log, message);
}
warning(...message: any[]) {
this.message(LoggingTypes.Warning, message);
}
logWithCustomColors(type: LoggingTypes, colors: string, ...message: any[]) {
this.message(type, message, colors);
}
error(error: Error | string) {
if (typeof error === "string") { if (typeof error === "string") {
let e = new Error() let e = new Error()
Logging.message(LoggingTypes.Error, [error, ":", e.stack]); this.message(LoggingTypes.Error, [error, ":", e.stack]);
} else { } else {
Logging.message(LoggingTypes.Error, [error.name, ":", error.message, ":", error.stack]); this.message(LoggingTypes.Error, [error.name, ":", error.message, ":", error.stack]);
} }
} }
static errorMessage(...message: any[]) { errorMessage(...message: any[]) {
Logging.message(LoggingTypes.Error, message); this.message(LoggingTypes.Error, message);
} }
private static async message(type: LoggingTypes, message: any[] | string, customColors?: string) { private async message(type: LoggingTypes, message: any[] | string, customColors?: string) {
var consoleLogFormat = Reset; var consoleLogFormat = Reset;
if (!customColors) { if (!customColors) {
switch (type) { switch (type) {
@ -116,9 +156,12 @@ export class Logging {
} }
let file = getCallerFile() let file = getCallerFile()
let date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''); let date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
var m = `[${LoggingTypes[type]}][${file.file}:${file.line}][${date}]: ${mb}`; let prefix = `[${LoggingTypes[type]}][${file.file}:${file.line}][${date}]: `;
if (this.stdout) console.log(consoleLogFormat + m + Reset); let message_lines = mb.split("\n").map(line => prefix + line);
if (this.config.console_out) message_lines.forEach(line => console.log(consoleLogFormat + line + Reset));
let m = message_lines.join("\n");
let index = m.indexOf("\x1b"); let index = m.indexOf("\x1b");
while (index >= 0) { while (index >= 0) {
m = m.substring(0, index) + m.substring(index + 5, m.length); m = m.substring(0, index) + m.substring(index + 5, m.length);
@ -126,111 +169,107 @@ export class Logging {
} }
m = m.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ""); m = m.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
this.writeMessageToFile(m, type === LoggingTypes.Error);
if (this.logFileLocation) { this.events.emit("message", { type: type, message: mb });
if ((!this.fileStream || !this.errorStream) && !this.writing) {
Logging.initializeFile();
}
Logging.writeMessageToFile(m, type === LoggingTypes.Error);
}
Logging.events.emit("message", { type: type, message: mb });
} }
private static writeMessageToFile(message: string, error?: boolean) { private writeMessageToFile(message: string, error?: boolean) {
Logging.queue.push({ message: message.replace("\n", " "), error: error }); if (!this.writeLock.locked && !this.fileStream && !(error || this.errorStream)) return;
Logging.checkQueue(); this.queue.push({ message: message.replace("\n", " "), error: error });
this.checkQueue();
} }
private static async checkQueue() { private async checkQueue() {
try { try {
if (Logging.writing) return; if (this.writeLock.locked) return;
if (Logging.queue.length <= 0) return; if (this.queue.length <= 0) return;
Logging.writing = true; let lock = await this.writeLock.getLock();
var message = Logging.queue[0]; var message = this.queue.shift();
message.message += "\n"; message.message += "\n";
let data = new Buffer(message.message, "utf8"); let data = new Buffer(message.message, "utf8");
if (data.byteLength < maxFileSize && data.byteLength + Logging.fileSize > maxFileSize) { await this.writeToLogFile(data);
Logging.fileStream.close(); if (message.error) await this.writeToErrorFile(data);
if (await fsExists(this.logFileLocation + "all.log.old")) lock.release();
await fsUnlink(this.logFileLocation + "all.log.old"); if (this.queue.length > 0) this.checkQueue();
await fsMove(this.logFileLocation + "all.log", this.logFileLocation + "all.log.old")
Logging.fileStream = fs.createWriteStream(this.logFileLocation + "all.log");
Logging.fileSize = 0;
}
Logging.fileSize += data.byteLength;
Logging.fileStream.write(data, async () => {
if (message.error) {
if (data.byteLength < maxFileSize && data.byteLength + Logging.errorSize > maxFileSize) {
Logging.errorStream.close();
if (await fsExists(this.logFileLocation + "error.log.old"))
await fsUnlink(this.logFileLocation + "error.log.old");
await fsMove(this.logFileLocation + "error.log", this.logFileLocation + "error.log.old")
Logging.errorStream = fs.createWriteStream(this.logFileLocation + "error.log");
Logging.errorSize = 0;
}
Logging.errorSize += data.byteLength;
Logging.errorStream.write(data, () => {
Logging.queue.splice(Logging.queue.indexOf(message), 1);
Logging.writing = false;
Logging.checkQueue();
});
} else {
Logging.queue.splice(Logging.queue.indexOf(message), 1);
Logging.writing = false;
Logging.checkQueue();
}
});
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
} }
private static async initializeFile() { private async writeToLogFile(data: Buffer) {
if (this.fileStream && this.errorStream) return; if (data.byteLength < maxFileSize && this.fileSize + data.byteLength > maxFileSize) {
if (!this.logFileLocation) return; let f = await this.initializeFile(this.config.logfile, true);
this.writing = true; this.fileStream = f.stream;
this.fileSize = f.size;
}
this.fileSize += data.byteLength;
this.fileStream.write(data);
}
private async writeToErrorFile(data: Buffer) {
if (data.byteLength < maxFileSize && this.errorSize + data.byteLength > maxFileSize) {
let f = await this.initializeFile(this.config.errorfile, true);
this.errorStream = f.stream;
this.errorSize = f.size;
}
this.errorSize += data.byteLength;
this.errorStream.write(data);
}
private async initializeFile(file: string, new_file = true): Promise<{ stream: fs.WriteStream, size: number }> {
try { try {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
fs.exists(this.logFileLocation, (exists) => { const folder = path.dirname(file);
if (!exists) { if (folder)
fs.mkdir(this.logFileLocation, (err) => { fs.exists(folder, (exists) => {
if (err) { if (!exists) {
reject(err); fs.mkdir(folder, (err) => {
} else { if (err) {
resolve(); reject(err);
} } else {
}); resolve();
} else resolve(); }
}); });
} else resolve();
});
}); });
if (await fsExists(this.logFileLocation + "all.log")) { let size = 0;
if (await fsExists(this.logFileLocation + "all.log.old")) if (await fsExists(file)) {
await fsUnlink(this.logFileLocation + "all.log.old"); let stats = await fsStat(file);
await fsMove(this.logFileLocation + "all.log", this.logFileLocation + "all.log.old") if (new_file || stats.size > maxFileSize) {
} if (await fsExists(file + ".old"))
await fsUnlink(file + ".old");
if (await fsExists(this.logFileLocation + "error.log")) { await fsMove(file, file + ".old")
let stats = await fsStat(this.logFileLocation + "error.log")
if (stats.size > maxFileSize) {
if (await fsExists(this.logFileLocation + "error.log.old"))
await fsUnlink(this.logFileLocation + "error.log.old");
await fsMove(this.logFileLocation + "error.log", this.logFileLocation + "error.log.old")
} else { } else {
this.errorSize = stats.size; size = stats.size;
} }
} }
this.fileStream = fs.createWriteStream(this.logFileLocation + "all.log", { flags: "a" }); return { stream: fs.createWriteStream(file, { flags: "a" }), size: size };
this.errorStream = fs.createWriteStream(this.logFileLocation + "error.log", { flags: "a" });
this.writing = false; // if (await fsExists(this.logFileLocation + "error.log")) {
this.checkQueue(); // let stats = await fsStat(this.logFileLocation + "error.log")
// if (stats.size > maxFileSize) {
// if (await fsExists(this.logFileLocation + "error.log.old"))
// await fsUnlink(this.logFileLocation + "error.log.old");
// await fsMove(this.logFileLocation + "error.log", this.logFileLocation + "error.log.old")
// } else {
// this.errorSize = stats.size;
// }
// }
// this.fileStream = fs.createWriteStream(this.logFileLocation + "all.log", { flags: "a" });
// this.errorStream = fs.createWriteStream(this.logFileLocation + "error.log", { flags: "a" });
// this.writing = false;
// this.checkQueue();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
return { size: 0, stream: undefined };
} }
} }
export const Logging = new LoggingBase();
export default Logging; export default Logging;
function fsUnlink(path) { function fsUnlink(path) {

36
src/lock.ts Normal file
View File

@ -0,0 +1,36 @@
export type Release = { release: () => void };
export default class Lock {
private _locked: boolean = false;
get locked() {
return this._locked;
}
private toCome: (() => void)[] = [];
constructor() {
this.release = this.release.bind(this);
}
async getLock(): Promise<Release> {
if (!this._locked) return { release: this.lock() };
else {
return new Promise<Release>((resolve) => {
this.toCome.push(() => {
resolve({ release: this.lock() });
})
})
}
}
private lock() {
this._locked = true;
return this.release;
}
private async release() {
if (this.toCome.length > 0) {
this.toCome.shift()();
} else {
this._locked = false;
}
}
}

View File

@ -11,8 +11,9 @@ Logging.log("\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m TEST \x1b[31m\x1b[
let err = new Error() let err = new Error()
if (typeof err.stack !== "string") console.log("Stacktrace invalid", err.stack) if (typeof err.stack !== "string") console.log("Stacktrace invalid", err.stack)
Logging.console_out = false;
Logging.stdout = false; Logging.waitForSetup().then(() => {
for (let i = 0; i < 7000; i++) { for (let i = 0; i < 7000; i++) {
Logging.log(randomBytes(50000).toString("hex")) Logging.log(randomBytes(50000).toString("hex"))
} }
});