Compare commits
11 Commits
96d7808f35
...
master
Author | SHA1 | Date | |
---|---|---|---|
3de5f368ef | |||
e0b51625d8 | |||
153aca0ccb | |||
7ca0c4fd72 | |||
8e183ac1a5 | |||
feed4626e6 | |||
9d4e521619 | |||
7d75f65dd3 | |||
eeed068ddd | |||
6daf815ea8 | |||
357b98c69a |
54
benchmark.js
Normal file
54
benchmark.js
Normal file
@ -0,0 +1,54 @@
|
||||
const { LoggingBase } = require("./out/index.js");
|
||||
let results = {};
|
||||
|
||||
function benchmark(name, count, runner) {
|
||||
console.profile(name);
|
||||
const start = process.hrtime.bigint()
|
||||
|
||||
runner(count);
|
||||
|
||||
const diffNS = process.hrtime.bigint() - start;
|
||||
const diffMS = Number(diffNS / 1000n / 1000n);
|
||||
console.profileEnd(name)
|
||||
|
||||
results[name]= {
|
||||
count,
|
||||
time: diffMS,
|
||||
timePerI: (diffMS / count).toFixed(4)
|
||||
}
|
||||
}
|
||||
|
||||
benchmark("simple", 10000000, (cnt) => {
|
||||
const l = new LoggingBase({
|
||||
console: false
|
||||
});
|
||||
for (let i = 0; i < cnt; i++) {
|
||||
l.log("simple log")
|
||||
}
|
||||
})
|
||||
|
||||
benchmark("complex", 1000000, (cnt) => {
|
||||
const l = new LoggingBase({
|
||||
console: false
|
||||
});
|
||||
for (let i = 0; i < cnt; i++) {
|
||||
l.log("complex log", {
|
||||
a: 1,
|
||||
b: {
|
||||
c:"test"
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
benchmark("very long", 10000000, (cnt) => {
|
||||
const l = new LoggingBase({
|
||||
console: false
|
||||
});
|
||||
const longText = "complex log".repeat(100);
|
||||
for (let i = 0; i < cnt; i++) {
|
||||
l.log(longText)
|
||||
}
|
||||
})
|
||||
|
||||
console.table(results)
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "logging",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.2",
|
||||
"description": "",
|
||||
"author": "Fabian Stamm <dev@fabianstamm.de>",
|
||||
"contributors": [],
|
||||
|
3943
package-lock.json
generated
3943
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@ -1,15 +1,18 @@
|
||||
{
|
||||
"name": "@hibas123/logging",
|
||||
"version": "3.0.0",
|
||||
"version": "4.0.1",
|
||||
"description": "",
|
||||
"main": "out/index.js",
|
||||
"types": "out/index.d.ts",
|
||||
"type": "module",
|
||||
"main": "esm/index.js",
|
||||
"types": "esm/index.d.ts",
|
||||
"module": "esm/index.js",
|
||||
"scripts": {
|
||||
"prepublish": "npm run build",
|
||||
"build": "tsc && tsc -p tsconfig.esm.json",
|
||||
"test": "tsc && node out/test.js",
|
||||
"postpublish": "denreg publish"
|
||||
"prepublishOnly": "npm run build",
|
||||
"build": "tsc",
|
||||
"test": "tsc && node esm/test.js",
|
||||
"postpublish": "denreg publish",
|
||||
"bench": "tsc && node benchmark.js",
|
||||
"prof": "tsc && ndb --prof benchmark.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -19,16 +22,15 @@
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"src/",
|
||||
"out/",
|
||||
"esm/",
|
||||
"tsconfig.json",
|
||||
"readme.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"concurrently": "^6.0.2",
|
||||
"ndb": "^1.1.5",
|
||||
"nodemon": "^2.0.7",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.2.4"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
124
src/base.ts
124
src/base.ts
@ -6,28 +6,13 @@ import {
|
||||
Format,
|
||||
FormatConfig,
|
||||
Formatted,
|
||||
ILoggingInterface,
|
||||
LoggingTypes,
|
||||
Message,
|
||||
} from "./types.js";
|
||||
|
||||
const browser = typeof window !== "undefined";
|
||||
|
||||
export interface ILoggingTimer {
|
||||
end: () => number;
|
||||
}
|
||||
|
||||
export interface ILoggingInterface {
|
||||
debug(...message: any[]): void;
|
||||
log(...message: any[]): void;
|
||||
warn(...message: any[]): void;
|
||||
error(...message: any[]): void;
|
||||
|
||||
time(name?: string): ILoggingTimer;
|
||||
timeEnd(id: string): number;
|
||||
|
||||
getChild(name: string): ILoggingInterface;
|
||||
}
|
||||
|
||||
export interface ILoggingOptions {
|
||||
/**
|
||||
* Name will be prefixed on Console output and added to logfiles, if not specified here
|
||||
@ -89,6 +74,12 @@ export abstract class LoggingInterface implements ILoggingInterface {
|
||||
|
||||
constructor(names: string[]) {
|
||||
this.#names = names;
|
||||
|
||||
for (const key in this) {
|
||||
if (typeof this[key] === "function") {
|
||||
this[key] = (this[key] as never as Function).bind(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(...message: any[]) {
|
||||
@ -134,7 +125,7 @@ export abstract class LoggingInterface implements ILoggingInterface {
|
||||
|
||||
this.#timerMap.set(id, {
|
||||
name: id,
|
||||
start: Logging.nativeFunctions.startTimer(),
|
||||
start: LoggingBase.nativeFunctions.startTimer(),
|
||||
});
|
||||
|
||||
return {
|
||||
@ -146,7 +137,7 @@ export abstract class LoggingInterface implements ILoggingInterface {
|
||||
timeEnd(id: string) {
|
||||
let timer = this.#timerMap.get(id);
|
||||
if (timer) {
|
||||
let diff = Logging.nativeFunctions.endTimer(timer.start);
|
||||
let diff = LoggingBase.nativeFunctions.endTimer(timer.start);
|
||||
|
||||
this.message(LoggingTypes.Debug, this.#names, [
|
||||
Format.green(`[${timer.name}]`),
|
||||
@ -163,13 +154,13 @@ export abstract class LoggingInterface implements ILoggingInterface {
|
||||
abstract getChild(name: string): ILoggingInterface;
|
||||
}
|
||||
|
||||
export class Logging extends LoggingInterface {
|
||||
export class LoggingBase extends LoggingInterface {
|
||||
private static [InitialisedAdapters] = new Map<Adapter, number>();
|
||||
public static nativeFunctions: INativeFunctions = DefaultNativeFunctions;
|
||||
|
||||
static DecoupledLogging = class extends LoggingInterface {
|
||||
#lg: Logging;
|
||||
constructor(names: string[], lg: Logging) {
|
||||
#lg: LoggingBase;
|
||||
constructor(names: string[], lg: LoggingBase) {
|
||||
super(names);
|
||||
this.#lg = lg;
|
||||
}
|
||||
@ -179,7 +170,10 @@ export class Logging extends LoggingInterface {
|
||||
}
|
||||
|
||||
getChild(name: string) {
|
||||
return new Logging.DecoupledLogging([this.names, name], this.#lg);
|
||||
return new LoggingBase.DecoupledLogging(
|
||||
[...this.names, name],
|
||||
this.#lg
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -208,12 +202,6 @@ export class Logging extends LoggingInterface {
|
||||
if (options.console) {
|
||||
this.addAdapter(consoleAdapter);
|
||||
}
|
||||
|
||||
for (const key in this) {
|
||||
if (typeof this[key] === "function") {
|
||||
this[key] = ((this[key] as never) as Function).bind(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async addAdapter(adapter: Adapter) {
|
||||
@ -221,25 +209,25 @@ export class Logging extends LoggingInterface {
|
||||
|
||||
const add = () => {
|
||||
this.#adapters.add(adapter);
|
||||
if (Logging[InitialisedAdapters].has(adapter)) {
|
||||
Logging[InitialisedAdapters].set(
|
||||
if (LoggingBase[InitialisedAdapters].has(adapter)) {
|
||||
LoggingBase[InitialisedAdapters].set(
|
||||
adapter,
|
||||
Logging[InitialisedAdapters].get(adapter) + 1
|
||||
LoggingBase[InitialisedAdapters].get(adapter) + 1
|
||||
);
|
||||
} else {
|
||||
Logging[InitialisedAdapters].set(adapter, 1);
|
||||
LoggingBase[InitialisedAdapters].set(adapter, 1);
|
||||
}
|
||||
};
|
||||
|
||||
if (!init) {
|
||||
add();
|
||||
} else {
|
||||
Promise.resolve(init).then(add);
|
||||
await Promise.resolve(init).then(add);
|
||||
}
|
||||
}
|
||||
|
||||
getChild(name: string): ILoggingInterface {
|
||||
return new Logging.DecoupledLogging([...this.names, name], this);
|
||||
return new LoggingBase.DecoupledLogging([...this.names, name], this);
|
||||
}
|
||||
|
||||
protected message(
|
||||
@ -255,7 +243,8 @@ export class Logging extends LoggingInterface {
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
const date_str = date.toISOString().replace(/T/, " ").replace(/\..+/, "");
|
||||
const isoStr = date.toISOString();
|
||||
const date_str = isoStr.substring(0, 10) + " " + isoStr.substring(11, 19);
|
||||
|
||||
let file: string | undefined = undefined;
|
||||
if (this.#resolve_filename) {
|
||||
@ -331,23 +320,14 @@ export class Logging extends LoggingInterface {
|
||||
new Formatted(": "),
|
||||
];
|
||||
|
||||
// let linePrefix = [
|
||||
// ...namePrefix,
|
||||
// new Formatted(date_str, this.#format_map.date),
|
||||
// new Formatted(" "),
|
||||
// new Formatted(type_str, type_format),
|
||||
// ...(file
|
||||
// ? [new Formatted(" "), new Formatted(file, this.#format_map.file)]
|
||||
// : []),
|
||||
// new Formatted("| "),
|
||||
// ];
|
||||
|
||||
let formattedMessage: Formatted<string>[] = [...linePrefix];
|
||||
message.forEach((msg, idx) => {
|
||||
let format = new Formatted();
|
||||
let format: Formatted;
|
||||
if (msg instanceof Formatted) {
|
||||
format = msg;
|
||||
msg = msg.content;
|
||||
} else {
|
||||
format = new Formatted();
|
||||
}
|
||||
|
||||
if (typeof msg !== "string") {
|
||||
@ -358,18 +338,17 @@ export class Logging extends LoggingInterface {
|
||||
}) as string;
|
||||
}
|
||||
|
||||
removeColors(msg)
|
||||
.split("\n")
|
||||
.forEach((text, index, { length }) => {
|
||||
if (index != length - 1) {
|
||||
formattedMessage.push(
|
||||
new Formatted(text + "\n", format),
|
||||
...linePrefix
|
||||
);
|
||||
} else {
|
||||
formattedMessage.push(new Formatted(text, format));
|
||||
}
|
||||
});
|
||||
// removeColors(msg) // Remove colors is uncommented for now, since there are no real benefits of having it and it reduces performance
|
||||
msg.split("\n").forEach((text, index, { length }) => {
|
||||
if (index != length - 1) {
|
||||
formattedMessage.push(
|
||||
new Formatted(text + "\n", format),
|
||||
...linePrefix
|
||||
);
|
||||
} else {
|
||||
formattedMessage.push(new Formatted(text, format));
|
||||
}
|
||||
});
|
||||
formattedMessage.push(new Formatted(" "));
|
||||
});
|
||||
|
||||
@ -381,26 +360,30 @@ export class Logging extends LoggingInterface {
|
||||
type,
|
||||
};
|
||||
|
||||
this.#adapters.forEach((adapter) => adapter.onMessage(msg));
|
||||
this.#adapters.forEach((adapter) => {
|
||||
if (adapter.level <= type) {
|
||||
adapter.onMessage(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.#closed) return;
|
||||
this.#closed = true;
|
||||
|
||||
this.#adapters.forEach((adapter) => {
|
||||
const cnt = Logging[InitialisedAdapters].get(adapter);
|
||||
for (const adapter of this.#adapters) {
|
||||
const cnt = LoggingBase[InitialisedAdapters].get(adapter);
|
||||
if (!cnt) {
|
||||
//TODO: should not happen!
|
||||
} else {
|
||||
if (cnt <= 1) {
|
||||
adapter.close();
|
||||
Logging[InitialisedAdapters].delete(adapter);
|
||||
if (adapter.close) await adapter.close();
|
||||
LoggingBase[InitialisedAdapters].delete(adapter);
|
||||
} else {
|
||||
Logging[InitialisedAdapters].set(adapter, cnt - 1);
|
||||
LoggingBase[InitialisedAdapters].set(adapter, cnt - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,9 +429,11 @@ function getCallerFile() {
|
||||
return { file: undefined, line: 0 };
|
||||
}
|
||||
|
||||
function getCallerFromExisting(
|
||||
err: Error
|
||||
): { file: string; line: number; column?: number } {
|
||||
function getCallerFromExisting(err: Error): {
|
||||
file: string;
|
||||
line: number;
|
||||
column?: number;
|
||||
} {
|
||||
if (!err || !err.stack) return { file: "NOFILE", line: 0 };
|
||||
let lines = err.stack.split("\n");
|
||||
lines.shift(); // removing first line
|
||||
@ -479,6 +464,7 @@ function getCallerFromExisting(
|
||||
|
||||
export function removeColors(text: string) {
|
||||
text = text.replace(
|
||||
// Putting regex here directly instead of externally actually improves performance. The cause of that is not clear for now.
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
||||
""
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
TerminalFormats,
|
||||
Formatted,
|
||||
IFormatted,
|
||||
LoggingTypes,
|
||||
} from "./types.js";
|
||||
|
||||
declare const Deno: any;
|
||||
@ -15,11 +16,15 @@ const deno = typeof Deno !== "undefined";
|
||||
const NodeJS = typeof process !== undefined;
|
||||
|
||||
export class ConsoleAdapter implements Adapter {
|
||||
level: LoggingTypes = LoggingTypes.Debug;
|
||||
constructor(private colors: boolean = true) {}
|
||||
|
||||
init() {}
|
||||
flush() {}
|
||||
|
||||
setLevel(level: LoggingTypes) {
|
||||
this.level = level;
|
||||
}
|
||||
// TODO: Check if required!
|
||||
// private escape(text: string): string {
|
||||
// return text
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Logging as LoggingClass } from "./base.js";
|
||||
export { Logging } from "./base.js";
|
||||
import { LoggingBase } from "./base.js";
|
||||
export { LoggingBase } from "./base.js";
|
||||
export { ConsoleAdapter } from "./consolewriter.js";
|
||||
export { ILoggingOptions, INativeFunctions } from "./base.js";
|
||||
|
||||
@ -14,8 +14,10 @@ export {
|
||||
TerminalFormats,
|
||||
Formatted,
|
||||
IFormatted,
|
||||
ILoggingInterface,
|
||||
ILoggingTimer,
|
||||
} from "./types.js";
|
||||
|
||||
const Logging = new LoggingClass();
|
||||
const Logging = new LoggingBase();
|
||||
|
||||
export default Logging;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Logging from "./index.js";
|
||||
|
||||
import { Logging as LoggingBase, LoggingTypes, Format } from "./index.js";
|
||||
import { LoggingBase, LoggingTypes, Format } from "./index.js";
|
||||
|
||||
Logging.log("test");
|
||||
Logging.log("i", "am", { a: "an" }, 1000);
|
||||
|
22
src/types.ts
22
src/types.ts
@ -203,7 +203,7 @@ export class DefaultFormatConfig implements FormatConfig {
|
||||
|
||||
date = new Formatted()._color(IColors.NONE);
|
||||
file = new Formatted()._color(IColors.NONE);
|
||||
names = new Formatted()._bold()._color(IColors.GREEN);
|
||||
names = new Formatted()._bold()._color(IColors.CYAN);
|
||||
names_delimiter = new Formatted(" -> ")._bold();
|
||||
}
|
||||
|
||||
@ -216,6 +216,8 @@ export interface Message {
|
||||
}
|
||||
|
||||
export interface Adapter {
|
||||
readonly level: LoggingTypes;
|
||||
|
||||
/**
|
||||
* This function initialises the Adapter. It might be called multiple times, when added to multiple instances
|
||||
* @param observable An observable to subscribe to messages
|
||||
@ -225,6 +227,8 @@ export interface Adapter {
|
||||
flush(sync: true): void;
|
||||
flush(sync: false): void | Promise<void>;
|
||||
|
||||
setLevel(level: LoggingTypes): void;
|
||||
|
||||
onMessage(message: Message): void;
|
||||
|
||||
/**
|
||||
@ -234,3 +238,19 @@ export interface Adapter {
|
||||
*/
|
||||
close?(): void;
|
||||
}
|
||||
|
||||
export interface ILoggingTimer {
|
||||
end: () => number;
|
||||
}
|
||||
|
||||
export interface ILoggingInterface {
|
||||
debug(...message: any[]): void;
|
||||
log(...message: any[]): void;
|
||||
warn(...message: any[]): void;
|
||||
error(...message: any[]): void;
|
||||
|
||||
time(name?: string): ILoggingTimer;
|
||||
timeEnd(id: string): number;
|
||||
|
||||
getChild(name: string): ILoggingInterface;
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"target": "ES2017",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "esm"
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["src"]
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "esm",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": true,
|
||||
"outDir": "out",
|
||||
"declaration": true,
|
||||
"typeRoots": ["node_modules/@types"]
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["src"]
|
||||
|
Reference in New Issue
Block a user