Compare commits

..

25 Commits

Author SHA1 Message Date
d39e13dfe1 Add another strategy to logging, which reduces memory leak risk 2021-05-08 22:53:32 +02:00
0742490527 Changing filewrite behavior 2021-05-08 22:43:46 +02:00
c58d75129d Update LoggingBase name 2021-05-08 22:18:51 +02:00
249f701cb7 V3 2021-05-08 22:11:15 +02:00
f01a4ffb21 Updating dependencies 2020-05-05 18:34:23 +02:00
43b94c5c75 Updating dependencies 2020-04-21 01:04:27 +02:00
9600b46699 Updating dependencies 2020-04-20 17:16:28 +02:00
b3f6a6c3f2 Updating dependencies 2020-04-15 20:00:05 +02:00
090aa4629b Updating dependencies 2020-04-15 19:52:10 +02:00
558ddad800 Updating dependencies and increasing support of new adapterset architecture 2020-04-11 17:41:28 +02:00
6f002456f6 Updating dependencies 2020-04-11 16:49:28 +02:00
a8a388997f Updating dependencies 2020-04-09 18:40:22 +02:00
2a55549199 Updating dependencies 2020-04-09 18:34:44 +02:00
c7c968753f Updating filewriter to comply to new Adapter interface 2020-04-09 18:17:32 +02:00
c5098934a1 Adding nodes hrtimer to new time and timeEnd 2020-04-06 11:59:55 +02:00
922af328a3 Updating dependencies 2020-03-21 21:13:54 +01:00
b76d0083b6 Update dependencies 2020-03-01 15:21:25 +01:00
57f1b07944 Updating dependencies 2019-11-17 16:46:26 +01:00
a6a5eeded5 Updating Dependencies 2019-10-12 12:47:53 +02:00
2735bcba35 Updating Logging to support new features 2019-07-13 12:12:10 +02:00
4c656420ad Updating dependencies 2019-05-14 11:36:38 -04:00
cc73129373 Update @hibas123/logging 2019-04-29 14:52:02 -04:00
9c454b403f Applying fix from logging 2019-04-05 09:27:52 -04:00
5bbf0f74f5 Updating dependency 2019-04-05 09:06:05 -04:00
8bf0d4b798 Updating dependencies 2019-04-04 22:44:19 -04:00
7 changed files with 3256 additions and 2633 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

5561
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
{
"name": "@hibas123/nodelogging",
"version": "2.0.1",
"version": "3.0.6",
"description": "",
"main": "out/index.js",
"types": "out/index.d.ts",
"scripts": {
"prepublish": "tsc",
"prepublish": "npm run build",
"build": "tsc",
"watch-ts": "tsc --watch",
"watch-js": "nodemon out/test.js",
"watch": "concurrently npm:watch-*",
"test": "node out/test.js",
"test": "npm run build && node out/test.js",
"live": "nodemon out/test.js"
},
"repository": {
@ -19,14 +19,20 @@
},
"author": "Fabian Stamm",
"license": "MIT",
"files": [
"src/",
"out/",
"tsconfig.json",
"readme.md"
],
"devDependencies": {
"@types/node": "^11.13.0",
"concurrently": "^4.1.0",
"nodemon": "^1.17.4",
"typescript": "^3.4.1"
"@types/node": "^15.0.2",
"concurrently": "^6.0.2",
"nodemon": "^2.0.7",
"typescript": "^4.2.4"
},
"dependencies": {
"@hibas123/logging": "^2.0.0",
"@hibas123/utils": "^2.0.5"
"@hibas123/logging": "^3.0.6",
"@hibas123/utils": "^2.2.18"
}
}
}

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.
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
const CustomLogging = new LoggingBase(name | {
@ -53,7 +53,7 @@ To not use any logfiles just set files to false.
# 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
const Demo = new LoggingExtended("Demo");

View File

@ -1,21 +1,19 @@
import { Lock, ObservableInterface } from "@hibas123/utils";
import { Lock } from "@hibas123/utils";
import * as fs from "fs";
import * as path from "path";
import { Adapter, Message, LoggingTypes } from "@hibas123/logging";
import { Adapter, Message, Formatted } from "@hibas123/logging";
const MAX_FILE_SIZE = 500000000;
export class LoggingFiles implements Adapter {
file: Files;
constructor(filename: string, private error = false, private maxFileSize = MAX_FILE_SIZE) {
this.file = Files.getFile(filename);
}
constructor(private filename: string, private maxFileSize = MAX_FILE_SIZE) {}
init(observable: ObservableInterface<Message>) {
observable.subscribe(this.onMessage.bind(this));
return this.file.init(this.maxFileSize);
init() {
if (!this.file) {
this.file = Files.getFile(this.filename);
this.file.init(this.maxFileSize);
}
}
flush(sync: boolean) {
@ -23,21 +21,33 @@ export class LoggingFiles implements Adapter {
}
onMessage(message: Message) {
// Just ignore all non error messages, if this.error is set
if (this.error && message.type !== LoggingTypes.Error)
return;
let txt = message.text.formatted.map(fmt => fmt.map(f => f.text).join("") + "\n").join("");
let msg = Buffer.from(txt);
let msg = Buffer.from(Formatted.strip(message.text) + "\n");
this.file.write(msg);
}
close() {
this.file.close();
this.file = undefined;
}
}
//TODO: Optimise write path
const Debounce = (callback: () => void, iv = 500) => {
let to: any;
return {
trigger: () => {
if (!to) {
to = setTimeout(() => {
to = undefined;
callback();
}, iv);
}
},
};
};
export class Files {
private open = 0;
@ -57,20 +67,24 @@ export class Files {
private size: number = 0;
private stream: fs.WriteStream = undefined;
private lock = new Lock();
private debounce = Debounce(() => this.checkQueue);
public initialized = false;
#initialized = false;
private constructor(private file: string) { }
public get initlialized() {
return this.#initialized;
}
private constructor(private file: string) {}
public async init(maxFileSize: number) {
if (this.initialized)
return;
if (this.#initialized) return;
let lock = await this.lock.getLock();
this.maxFileSize == maxFileSize;
await this.initializeFile()
this.initialized = true;
await this.initializeFile();
this.#initialized = true;
lock.release();
this.checkQueue()
this.checkQueue();
}
private async initializeFile(new_file = false) {
@ -80,8 +94,8 @@ export class Files {
}
const folder = path.dirname(this.file);
if (folder) {
if (!await fsExists(folder)) {
await fsMkDir(folder).catch(() => { }); //Could happen, if two seperate instances want to create the same folder so ignoring
if (!(await fsExists(folder))) {
await fsMkDir(folder).catch(() => {}); //Could happen, if two seperate instances want to create the same folder so ignoring
}
}
@ -91,17 +105,17 @@ export class Files {
if (new_file || stats.size >= this.maxFileSize) {
if (await fsExists(this.file + ".old"))
await fsUnlink(this.file + ".old");
await fsMove(this.file, this.file + ".old")
await fsMove(this.file, this.file + ".old");
} else {
size = stats.size;
}
}
this.stream = fs.createWriteStream(this.file, { flags: "a" })
this.stream = fs.createWriteStream(this.file, { flags: "a" });
this.size = size;
} catch (e) {
console.log(e);
//ToDo is this the right behavior?
//TODO: is this the right behavior?
process.exit(1);
}
}
@ -112,7 +126,7 @@ export class Files {
if (this.lock.locked) return;
let lock = await this.lock.getLock();
let msg: Buffer;
while (msg = this.queue.shift()) {
while ((msg = this.queue.shift())) {
await this.write_to_file(msg);
}
lock.release();
@ -122,15 +136,16 @@ export class Files {
await this.flush(false);
this.open--;
if (this.open <= 0) {
this.stream.close()
this.stream.close();
Files.files.delete(this.file);
}
}
public flush(sync: boolean) {
if (sync) {
// if sync flush, the process most likely is in failstate, so checkQueue stopped its work.
let msg: Buffer;
while (msg = this.queue.shift()) {
while ((msg = this.queue.shift())) {
this.stream.write(msg);
}
} else {
@ -138,14 +153,17 @@ export class Files {
const lock = await this.lock.getLock();
lock.release();
await this.checkQueue();
})
});
}
}
private async write_to_file(data: Buffer) {
try {
if (data.byteLength < this.maxFileSize && this.size + data.byteLength > this.maxFileSize) {
await this.initializeFile(true)
if (
data.byteLength < this.maxFileSize &&
this.size + data.byteLength > this.maxFileSize
) {
await this.initializeFile(true);
}
this.size += data.byteLength;
this.stream.write(data);
@ -159,21 +177,19 @@ export class Files {
public write(data: Buffer) {
this.queue.push(data);
this.checkQueue()
this.debounce.trigger();
}
public dispose() {
}
public dispose() {}
}
function fsUnlink(path) {
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
fs.unlink(path, (err) => {
if (err) reject(err);
else resolve();
})
})
});
});
}
function fsStat(path: string) {
@ -181,36 +197,36 @@ function fsStat(path: string) {
fs.stat(path, (err, stats) => {
if (err) reject(err);
else resolve(stats);
})
})
});
});
}
function fsMove(oldPath: string, newPath: string) {
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
let callback = (err?) => {
if (err) reject(err)
else resolve()
}
if (err) reject(err);
else resolve();
};
fs.rename(oldPath, newPath, function (err) {
if (err) {
if (err.code === 'EXDEV') {
if (err.code === "EXDEV") {
copy();
} else {
callback(err)
callback(err);
}
return;
}
callback()
callback();
});
function copy() {
fs.copyFile(oldPath, newPath, (err) => {
if (err) callback(err)
if (err) callback(err);
else fs.unlink(oldPath, callback);
})
});
}
})
});
}
function fsExists(path: string) {
@ -220,7 +236,7 @@ function fsExists(path: string) {
}
function fsMkDir(path: string) {
return new Promise((resolve, reject) => {
fs.mkdir(path, (err) => err ? reject(err) : resolve());
return new Promise<void>((resolve, reject) => {
fs.mkdir(path, (err) => (err ? reject(err) : resolve()));
});
}
}

View File

@ -1,58 +1,28 @@
export { LoggingFiles } from "./filewriter";
import { LoggingFiles } from "./filewriter";
import { LoggingBase as LoggingBaseOriginal, LoggingBaseOptions } from "@hibas123/logging";
import { LoggingBase } from "@hibas123/logging";
import Logging from "@hibas123/logging";
export interface LoggingOptions extends LoggingBaseOptions {
files: boolean | {
/**
* Filename/path of the logfile. Skip if generated with name.
*
* If not wanted pass null
*/
logfile?: string | null;
/**
* Filename/path of the logfile. Skip if generated with name.
*
* If not wanted pass null
*/
errorfile?: string | null;
}
}
export class LoggingBase extends LoggingBaseOriginal {
constructor(config: Partial<LoggingOptions> | string = {}) {
super(config);
if (typeof config === "string" || config.files !== false) {
let logfile: string;
let errorfile: string;
if (typeof config !== "string" && typeof config.files === "object") {
logfile = config.files.logfile;
errorfile = config.files.errorfile;
}
let name = this.name ? "." + this.name : "";
if (!logfile && logfile !== null)
logfile = `./logs/all${name}.log`;
if (!errorfile && errorfile !== null)
errorfile = `./logs/error${name}.log`;
if (logfile)
this.addAdapter(new LoggingFiles(logfile));
if (errorfile)
this.addAdapter(new LoggingFiles(errorfile, true));
LoggingBase.nativeFunctions = {
startTimer: () => {
if (process.hrtime.bigint) {
return process.hrtime.bigint();
} else {
return process.hrtime();
}
}
}
},
endTimer: (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;
}
},
};
export const DefaultLoggingFile = new LoggingFiles("./logs/all.log");
Logging.addAdapter(DefaultLoggingFile);
export let Logging: LoggingBase = undefined;
if (process.env.LOGGING_NO_DEFAULT !== "true") {
Logging = new LoggingBase();
}
export default Logging;

View File

@ -1,14 +1,17 @@
import { randomBytes } from "crypto";
import * as fs from "fs";
import { Logging, LoggingBase } from ".";
import { LoggingBase } from "@hibas123/logging";
import Logging, { DefaultLoggingFile } from ".";
const deleteFolderRecursive = function (path: string) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(function (file, index) {
var curPath = path + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) { // recurse
if (fs.lstatSync(curPath).isDirectory()) {
// recurse
deleteFolderRecursive(curPath);
} else { // delete file
} else {
// delete file
fs.unlinkSync(curPath);
}
});
@ -16,61 +19,72 @@ const deleteFolderRecursive = function (path: string) {
}
};
deleteFolderRecursive("./logs")
deleteFolderRecursive("./logs");
Logging.log("test")
Logging.log("test");
Logging.log("i", "am", { a: "an" }, 1000);
Logging.error(new Error("fehler 001"));
Logging.debug("Some Debug infos");
Logging.errorMessage("i", "am", "an", "error");
Logging.error("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()
if (typeof err.stack !== "string") console.log("Stacktrace invalid", err.stack)
let err = new Error();
if (typeof err.stack !== "string") console.log("Stacktrace invalid", err.stack);
let cus = new LoggingBase({ name: "test" });
cus.log("Hello from custom Logger")
cus.log("Hello from custom Logger");
let cus2 = new LoggingBase("test2");
cus2.log("Hello from custom Logger 2")
let cus2 = Logging.getChild("test2");
cus2.log("Hello from custom Logger 2");
let cus22 = new LoggingBase("test2");
cus22.log("Hello from custom Logger 22")
cus2.log("Hello from custom Logger 2")
cus22.log("Hello from custom Logger 22")
cus2.log("Hello from custom Logger 2")
cus22.log("Hello from custom Logger 22")
cus2.log("Hello from custom Logger 2")
cus22.log("Hello from custom Logger 22")
cus2.log("Hello from custom Logger 2")
cus22.log("Hello from custom Logger 22")
cus2.log("Hello from custom Logger 2")
let cus22 = Logging.getChild("test2");
cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2");
cus22.log("Hello from custom Logger 22");
cus2.log("Hello from custom Logger 2");
const BenchmarkLogger = new LoggingBase({
console: false,
name: "bench"
})
name: "bench",
});
BenchmarkLogger.addAdapter(DefaultLoggingFile);
async function benchmark(count: number, message_size: number) {
await BenchmarkLogger.waitForSetup();
const randData = randomBytes(message_size).toString("hex")
const randData = randomBytes(message_size).toString("hex");
const t = process.hrtime();
for (let i = 0; i < count; i++) {
BenchmarkLogger.log(randData)
BenchmarkLogger.log(randData);
}
const diff = process.hrtime(t);
const NS_PER_SEC = 1e9;
await BenchmarkLogger.waitForSetup();
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(`This is equal to ${(ns / 1000000) / count} ms per message`)
console.log(
`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 () => {
return;
console.log("Large data benchmark:")
const benchTimer = Logging.time("benchmark");
Promise.resolve().then(async () => {
console.log("Large data benchmark:");
await benchmark(7000, 50000);
console.log("Realdata data benchmark:")
await benchmark(100000, 100)
});
console.log("Realdata data benchmark:");
await benchmark(100000, 100);
benchTimer.end();
});
const timer = Logging.time("Test Timer");
setTimeout(() => timer.end(), 1000);