Compare commits

...

18 Commits

7 changed files with 479 additions and 2199 deletions

6
.editorconfig Normal file
View File

@ -0,0 +1,6 @@
[*]
charset = utf-8
indent_style = space
indent_size = 3
trim_trailing_whitespace = true
end_of_line = lf

2397
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@hibas123/nodelogging", "name": "@hibas123/nodelogging",
"version": "2.0.1", "version": "2.4.1",
"description": "", "description": "",
"main": "out/index.js", "main": "out/index.js",
"types": "out/index.d.ts", "types": "out/index.d.ts",
@ -19,14 +19,20 @@
}, },
"author": "Fabian Stamm", "author": "Fabian Stamm",
"license": "MIT", "license": "MIT",
"files": [
"src/",
"out/",
"tsconfig.json",
"readme.md"
],
"devDependencies": { "devDependencies": {
"@types/node": "^11.13.0", "@types/node": "^13.11.0",
"concurrently": "^4.1.0", "concurrently": "^5.1.0",
"nodemon": "^1.17.4", "nodemon": "^2.0.2",
"typescript": "^3.4.1" "typescript": "^3.8.3"
}, },
"dependencies": { "dependencies": {
"@hibas123/logging": "^2.0.0", "@hibas123/logging": "^2.4.1",
"@hibas123/utils": "^2.0.5" "@hibas123/utils": "^2.2.3"
} }
} }

View File

@ -31,7 +31,7 @@ All Logging types except the simple error take as many arguments as you want. Th
NodeLogging can work without any configuration, but it may be useful to change the log output folder. NodeLogging can work without any configuration, but it may be useful to change the log output folder.
Todo so you are capable of creating own instances of the LoggingBase class To do so you are capable of creating own instances of the LoggingBase class
``` javascript ``` javascript
const CustomLogging = new LoggingBase(name | { const CustomLogging = new LoggingBase(name | {
@ -53,7 +53,7 @@ To not use any logfiles just set files to false.
# Plugins # Plugins
There is a new Plugin API available, that makes is possible to add custom Logging Adapter. There is a Plugin API available, that makes is possible to add custom Logging Adapter.
``` javascript ``` javascript
const Demo = new LoggingExtended("Demo"); const Demo = new LoggingExtended("Demo");

View File

@ -3,20 +3,24 @@ import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import { Adapter, Message, LoggingTypes } from "@hibas123/logging"; import { Adapter, Message, LoggingTypes } from "@hibas123/logging";
const MAX_FILE_SIZE = 500000000; const MAX_FILE_SIZE = 500000000;
export class LoggingFiles implements Adapter { export class LoggingFiles implements Adapter {
file: Files; file: Files;
constructor(filename: string, private error = false, private maxFileSize = MAX_FILE_SIZE) { constructor(
this.file = Files.getFile(filename); private filename: string,
} private error = false,
private maxFileSize = MAX_FILE_SIZE,
private noPrefix = false
) {}
init(observable: ObservableInterface<Message>) { init(observable: ObservableInterface<Message>) {
observable.subscribe(this.onMessage.bind(this)); observable.subscribe(this.onMessage.bind(this));
if (!this.file) {
this.file = Files.getFile(this.filename);
return this.file.init(this.maxFileSize); return this.file.init(this.maxFileSize);
} }
}
flush(sync: boolean) { flush(sync: boolean) {
this.file.flush(sync); this.file.flush(sync);
@ -24,10 +28,13 @@ export class LoggingFiles implements Adapter {
onMessage(message: Message) { onMessage(message: Message) {
// Just ignore all non error messages, if this.error is set // Just ignore all non error messages, if this.error is set
if (this.error && message.type !== LoggingTypes.Error) if (this.error && message.type !== LoggingTypes.Error) return;
return;
let txt = message.text.formatted.map(fmt => fmt.map(f => f.text).join("") + "\n").join(""); let prefix = "";
if (message.name) prefix = `[${message.name}]=>`;
let txt = message.text.formatted
.map((fmt) => prefix + fmt.map((f) => f.text).join("") + "\n")
.join("");
let msg = Buffer.from(txt); let msg = Buffer.from(txt);
this.file.write(msg); this.file.write(msg);
@ -35,6 +42,7 @@ export class LoggingFiles implements Adapter {
close() { close() {
this.file.close(); this.file.close();
this.file = undefined;
} }
} }
@ -58,19 +66,22 @@ export class Files {
private stream: fs.WriteStream = undefined; private stream: fs.WriteStream = undefined;
private lock = new Lock(); private lock = new Lock();
public initialized = false; private $initialized = false;
private constructor(private file: string) { } public get initlialized() {
return this.$initialized;
}
private constructor(private file: string) {}
public async init(maxFileSize: number) { public async init(maxFileSize: number) {
if (this.initialized) if (this.$initialized) return;
return;
let lock = await this.lock.getLock(); let lock = await this.lock.getLock();
this.maxFileSize == maxFileSize; this.maxFileSize == maxFileSize;
await this.initializeFile() await this.initializeFile();
this.initialized = true; this.$initialized = true;
lock.release(); lock.release();
this.checkQueue() this.checkQueue();
} }
private async initializeFile(new_file = false) { private async initializeFile(new_file = false) {
@ -80,8 +91,8 @@ export class Files {
} }
const folder = path.dirname(this.file); const folder = path.dirname(this.file);
if (folder) { if (folder) {
if (!await fsExists(folder)) { if (!(await fsExists(folder))) {
await fsMkDir(folder).catch(() => { }); //Could happen, if two seperate instances want to create the same folder so ignoring await fsMkDir(folder).catch(() => {}); //Could happen, if two seperate instances want to create the same folder so ignoring
} }
} }
@ -91,13 +102,13 @@ export class Files {
if (new_file || stats.size >= this.maxFileSize) { if (new_file || stats.size >= this.maxFileSize) {
if (await fsExists(this.file + ".old")) if (await fsExists(this.file + ".old"))
await fsUnlink(this.file + ".old"); await fsUnlink(this.file + ".old");
await fsMove(this.file, this.file + ".old") await fsMove(this.file, this.file + ".old");
} else { } else {
size = stats.size; size = stats.size;
} }
} }
this.stream = fs.createWriteStream(this.file, { flags: "a" }) this.stream = fs.createWriteStream(this.file, { flags: "a" });
this.size = size; this.size = size;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -112,7 +123,7 @@ export class Files {
if (this.lock.locked) return; if (this.lock.locked) return;
let lock = await this.lock.getLock(); let lock = await this.lock.getLock();
let msg: Buffer; let msg: Buffer;
while (msg = this.queue.shift()) { while ((msg = this.queue.shift())) {
await this.write_to_file(msg); await this.write_to_file(msg);
} }
lock.release(); lock.release();
@ -122,7 +133,7 @@ export class Files {
await this.flush(false); await this.flush(false);
this.open--; this.open--;
if (this.open <= 0) { if (this.open <= 0) {
this.stream.close() this.stream.close();
Files.files.delete(this.file); Files.files.delete(this.file);
} }
} }
@ -130,7 +141,7 @@ export class Files {
if (sync) { if (sync) {
// if sync flush, the process most likely is in failstate, so checkQueue stopped its work. // if sync flush, the process most likely is in failstate, so checkQueue stopped its work.
let msg: Buffer; let msg: Buffer;
while (msg = this.queue.shift()) { while ((msg = this.queue.shift())) {
this.stream.write(msg); this.stream.write(msg);
} }
} else { } else {
@ -138,14 +149,17 @@ export class Files {
const lock = await this.lock.getLock(); const lock = await this.lock.getLock();
lock.release(); lock.release();
await this.checkQueue(); await this.checkQueue();
}) });
} }
} }
private async write_to_file(data: Buffer) { private async write_to_file(data: Buffer) {
try { try {
if (data.byteLength < this.maxFileSize && this.size + data.byteLength > this.maxFileSize) { if (
await this.initializeFile(true) data.byteLength < this.maxFileSize &&
this.size + data.byteLength > this.maxFileSize
) {
await this.initializeFile(true);
} }
this.size += data.byteLength; this.size += data.byteLength;
this.stream.write(data); this.stream.write(data);
@ -159,12 +173,10 @@ export class Files {
public write(data: Buffer) { public write(data: Buffer) {
this.queue.push(data); this.queue.push(data);
this.checkQueue() this.checkQueue();
} }
public dispose() { public dispose() {}
}
} }
function fsUnlink(path) { function fsUnlink(path) {
@ -172,8 +184,8 @@ function fsUnlink(path) {
fs.unlink(path, (err) => { fs.unlink(path, (err) => {
if (err) reject(err); if (err) reject(err);
else resolve(); else resolve();
}) });
}) });
} }
function fsStat(path: string) { function fsStat(path: string) {
@ -181,36 +193,36 @@ function fsStat(path: string) {
fs.stat(path, (err, stats) => { fs.stat(path, (err, stats) => {
if (err) reject(err); if (err) reject(err);
else resolve(stats); else resolve(stats);
}) });
}) });
} }
function fsMove(oldPath: string, newPath: string) { function fsMove(oldPath: string, newPath: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let callback = (err?) => { let callback = (err?) => {
if (err) reject(err) if (err) reject(err);
else resolve() else resolve();
} };
fs.rename(oldPath, newPath, function (err) { fs.rename(oldPath, newPath, function (err) {
if (err) { if (err) {
if (err.code === 'EXDEV') { if (err.code === "EXDEV") {
copy(); copy();
} else { } else {
callback(err) callback(err);
} }
return; return;
} }
callback() callback();
}); });
function copy() { function copy() {
fs.copyFile(oldPath, newPath, (err) => { fs.copyFile(oldPath, newPath, (err) => {
if (err) callback(err) if (err) callback(err);
else fs.unlink(oldPath, callback); else fs.unlink(oldPath, callback);
}) });
} }
}) });
} }
function fsExists(path: string) { function fsExists(path: string) {
@ -221,6 +233,6 @@ function fsExists(path: string) {
function fsMkDir(path: string) { function fsMkDir(path: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.mkdir(path, (err) => err ? reject(err) : resolve()); fs.mkdir(path, (err) => (err ? reject(err) : resolve()));
}); });
} }

View File

@ -1,10 +1,14 @@
export { LoggingFiles } from "./filewriter"; export { LoggingFiles } from "./filewriter";
import { LoggingFiles } from "./filewriter"; import { LoggingFiles } from "./filewriter";
import { LoggingBase as LoggingBaseOriginal, LoggingBaseOptions } from "@hibas123/logging"; import {
LoggingBase as LoggingBaseOriginal,
LoggingBaseOptions,
} from "@hibas123/logging";
export interface LoggingOptions extends LoggingBaseOptions { export interface LoggingOptions extends LoggingBaseOptions {
files: boolean | { files:
| boolean
| {
/** /**
* Filename/path of the logfile. Skip if generated with name. * Filename/path of the logfile. Skip if generated with name.
* *
@ -17,7 +21,7 @@ export interface LoggingOptions extends LoggingBaseOptions {
* If not wanted pass null * If not wanted pass null
*/ */
errorfile?: string | null; errorfile?: string | null;
} };
} }
export class LoggingBase extends LoggingBaseOriginal { export class LoggingBase extends LoggingBaseOriginal {
@ -33,17 +37,36 @@ export class LoggingBase extends LoggingBaseOriginal {
} }
let name = this.name ? "." + this.name : ""; let name = this.name ? "." + this.name : "";
if (!logfile && logfile !== null) if (!logfile && logfile !== null) logfile = `./logs/all${name}.log`;
logfile = `./logs/all${name}.log`;
if (!errorfile && errorfile !== null) if (!errorfile && errorfile !== null)
errorfile = `./logs/error${name}.log`; errorfile = `./logs/error${name}.log`;
if (logfile) if (logfile) this.addAdapter(new LoggingFiles(logfile));
this.addAdapter(new LoggingFiles(logfile));
if (errorfile) this.addAdapter(new LoggingFiles(errorfile, true));
}
}
if (errorfile) protected postGetChild(child: LoggingBase) {
this.addAdapter(new LoggingFiles(errorfile, true)); child.getCurrentTime = this.getCurrentTime.bind(child);
child.getTimeDiff = this.getTimeDiff.bind(child);
child.postGetChild = this.postGetChild.bind(child);
}
protected getCurrentTime() {
if (process.hrtime.bigint) {
return process.hrtime.bigint();
} else {
return process.hrtime();
}
}
protected getTimeDiff(start) {
if (process.hrtime.bigint) {
return Number((process.hrtime.bigint() - start) / BigInt(1000)) / 1000;
} else {
let diff = process.hrtime(start);
return diff[0] * 1000 + diff[1] / 1000000;
} }
} }
} }
@ -53,6 +76,3 @@ if (process.env.LOGGING_NO_DEFAULT !== "true") {
Logging = new LoggingBase(); Logging = new LoggingBase();
} }
export default Logging; export default Logging;

View File

@ -6,9 +6,11 @@ const deleteFolderRecursive = function (path: string) {
if (fs.existsSync(path)) { if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(function (file, index) { fs.readdirSync(path).forEach(function (file, index) {
var curPath = path + "/" + file; var curPath = path + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) { // recurse if (fs.lstatSync(curPath).isDirectory()) {
// recurse
deleteFolderRecursive(curPath); deleteFolderRecursive(curPath);
} else { // delete file } else {
// delete file
fs.unlinkSync(curPath); fs.unlinkSync(curPath);
} }
}); });
@ -16,61 +18,70 @@ const deleteFolderRecursive = function (path: string) {
} }
}; };
deleteFolderRecursive("./logs") deleteFolderRecursive("./logs");
Logging.log("test") Logging.log("test");
Logging.log("i", "am", { a: "an" }, 1000); Logging.log("i", "am", { a: "an" }, 1000);
Logging.error(new Error("fehler 001")); Logging.error(new Error("fehler 001"));
Logging.debug("Some Debug infos"); Logging.debug("Some Debug infos");
Logging.errorMessage("i", "am", "an", "error"); Logging.errorMessage("i", "am", "an", "error");
Logging.log("\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m TEST \x1b[31m\x1b[31m\x1b[31m") Logging.log(
"\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m\x1b[31m TEST \x1b[31m\x1b[31m\x1b[31m"
);
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);
let cus = new LoggingBase({ name: "test" }); let cus = new LoggingBase({ name: "test" });
cus.log("Hello from custom Logger") cus.log("Hello from custom Logger");
let cus2 = new LoggingBase("test2"); let cus2 = new LoggingBase("test2");
cus2.log("Hello from custom Logger 2") cus2.log("Hello from custom Logger 2");
let cus22 = new LoggingBase("test2"); let cus22 = new LoggingBase("test2");
cus22.log("Hello from custom Logger 22") cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2") cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22") cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2") cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22") cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2") cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22") cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2") cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22") cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2") cus2.log("Hello from custom Logger 2");
const BenchmarkLogger = new LoggingBase({ const BenchmarkLogger = new LoggingBase({
console: false, console: false,
name: "bench" name: "bench",
}) });
async function benchmark(count: number, message_size: number) { async function benchmark(count: number, message_size: number) {
await BenchmarkLogger.waitForSetup(); await BenchmarkLogger.waitForSetup();
const randData = randomBytes(message_size).toString("hex") const randData = randomBytes(message_size).toString("hex");
const t = process.hrtime(); const t = process.hrtime();
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
BenchmarkLogger.log(randData) BenchmarkLogger.log(randData);
} }
const diff = process.hrtime(t); const diff = process.hrtime(t);
const NS_PER_SEC = 1e9; const NS_PER_SEC = 1e9;
await BenchmarkLogger.waitForSetup(); await BenchmarkLogger.waitForSetup();
const ns = diff[0] * NS_PER_SEC + diff[1]; const ns = diff[0] * NS_PER_SEC + diff[1];
console.log(`Benchmark took ${ns / 1000000}ms for ${count} messages with a size of ${message_size} characters`); console.log(
console.log(`This is equal to ${(ns / 1000000) / count} ms per message`) `Benchmark took ${
ns / 1000000
}ms for ${count} messages with a size of ${message_size} characters`
);
console.log(`This is equal to ${ns / 1000000 / count} ms per message`);
} }
Logging.waitForSetup().then(async () => { Logging.waitForSetup().then(async () => {
return; return;
console.log("Large data benchmark:") console.log("Large data benchmark:");
await benchmark(7000, 50000); await benchmark(7000, 50000);
console.log("Realdata data benchmark:") console.log("Realdata data benchmark:");
await benchmark(100000, 100) await benchmark(100000, 100);
}); });
const timer = Logging.time("timer1", "Test Timer");
setTimeout(() => timer.end(), 1000);