Add time and timeEnd functionality

This commit is contained in:
Fabian Stamm 2020-04-06 11:47:59 +02:00
parent 675ec50139
commit cedfe2cced
5 changed files with 384 additions and 286 deletions

View File

@ -1,3 +1,4 @@
[*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 3 indent_size = 3

View File

@ -1,6 +1,6 @@
{ {
"name": "@hibas123/logging", "name": "@hibas123/logging",
"version": "2.1.4", "version": "2.2.0",
"description": "", "description": "",
"main": "out/index.js", "main": "out/index.js",
"types": "out/index.d.ts", "types": "out/index.d.ts",

View File

@ -1,10 +1,27 @@
import { Observable } from "@hibas123/utils"; import { Observable } from "@hibas123/utils";
import { ConsoleAdapter } from "./consolewriter"; import { ConsoleAdapter } from "./consolewriter";
import inspect from "./inspect"; import inspect from "./inspect";
import { Adapter, LoggingTypes, Message, FormatConfig, DefaultFormatConfig, Format, FormatTypes, Colors, FormattedText, FormattedLine } from "./types"; import {
Adapter,
LoggingTypes,
Message,
FormatConfig,
DefaultFormatConfig,
Format,
FormatTypes,
Colors,
FormattedText,
FormattedLine,
} from "./types";
import Logging from ".";
const browser = typeof window !== "undefined";
export function removeColors(text: string) { export function removeColors(text: string) {
text = text.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ""); text = text.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
""
);
// let index = text.indexOf("\x1b"); // let index = text.indexOf("\x1b");
// while (index >= 0) { // while (index >= 0) {
@ -18,7 +35,7 @@ export interface LoggingBaseOptions {
/** /**
* Name will be prefixed on Console output and added to logfiles, if not specified here * Name will be prefixed on Console output and added to logfiles, if not specified here
*/ */
name: string, name: string;
/** /**
* Prints output to console * Prints output to console
*/ */
@ -26,21 +43,20 @@ export interface LoggingBaseOptions {
} }
export class LoggingBase { export class LoggingBase {
private _formatMap: FormatConfig = new DefaultFormatConfig(); private _formatMap: FormatConfig = new DefaultFormatConfig();
public set formatMap(value: FormatConfig) { public set formatMap(value: FormatConfig) {
this._formatMap = value; this._formatMap = value;
} }
private adapter = new Set<Adapter>(); private adapter = new Set<Adapter>();
private adapter_init: Promise<void>[] = []; private adapter_init: Promise<void>[] = [];
private timerMap = new Map<string, { name: string; start: any }>();
private messageObservable = new Observable<Message>(); private messageObservable = new Observable<Message>();
protected _name: string; protected _name: string;
private _logLevel = LoggingTypes.Debug; private _logLevel = LoggingTypes.Debug;
get logLevel() { get logLevel() {
@ -57,7 +73,7 @@ export class LoggingBase {
constructor(options?: Partial<LoggingBaseOptions> | string) { constructor(options?: Partial<LoggingBaseOptions> | string) {
let opt: Partial<LoggingBaseOptions>; let opt: Partial<LoggingBaseOptions>;
if (!options) opt = {} if (!options) opt = {};
else if (typeof options === "string") { else if (typeof options === "string") {
opt = { name: options }; opt = { name: options };
} else { } else {
@ -67,21 +83,20 @@ export class LoggingBase {
let config: LoggingBaseOptions = { let config: LoggingBaseOptions = {
name: undefined, name: undefined,
console: true, console: true,
...opt ...opt,
}; };
if (config.name) if (config.name) this._name = config.name;
this._name = config.name;
for (let key in this) { for (let key in this) {
if (typeof this[key] === "function") this[key] = (<any>this[key]).bind(this); if (typeof this[key] === "function")
this[key] = (<any>this[key]).bind(this);
} }
if (config.console) { if (config.console) {
this.addAdapter(new ConsoleAdapter()); this.addAdapter(new ConsoleAdapter());
} }
//Binding function to this //Binding function to this
this.debug = this.debug.bind(this); this.debug = this.debug.bind(this);
this.log = this.log.bind(this); this.log = this.log.bind(this);
@ -95,7 +110,9 @@ export class LoggingBase {
addAdapter(adapter: Adapter) { addAdapter(adapter: Adapter) {
if (!this.adapter.has(adapter)) { if (!this.adapter.has(adapter)) {
this.adapter.add(adapter); this.adapter.add(adapter);
let prms = Promise.resolve(adapter.init(this.messageObservable.getPublicApi(), this._name)); let prms = Promise.resolve(
adapter.init(this.messageObservable.getPublicApi(), this._name)
);
this.adapter_init.push(prms); this.adapter_init.push(prms);
} }
} }
@ -104,16 +121,18 @@ export class LoggingBase {
flush(sync: false): Promise<void>; flush(sync: false): Promise<void>;
flush(sync: boolean): void | Promise<void> { flush(sync: boolean): void | Promise<void> {
if (sync) { if (sync) {
this.adapter.forEach(elm => elm.flush(true)); this.adapter.forEach((elm) => elm.flush(true));
} else { } else {
let adapters: (void | Promise<void>)[] = []; let adapters: (void | Promise<void>)[] = [];
this.adapter.forEach(elm => adapters.push(elm.flush(false))); this.adapter.forEach((elm) => adapters.push(elm.flush(false)));
return Promise.all(adapters).then(() => {}); return Promise.all(adapters).then(() => {});
} }
} }
public close() { public close() {
this.adapter.forEach(adapter => adapter.close ? adapter.close() : undefined); this.adapter.forEach((adapter) =>
adapter.close ? adapter.close() : undefined
);
} }
public waitForSetup() { public waitForSetup() {
@ -141,14 +160,18 @@ export class LoggingBase {
} }
error(error: Error | string) { error(error: Error | string) {
if (this._logLevel > LoggingTypes.Error) if (this._logLevel > LoggingTypes.Error) return;
return; if (!error)
if (!error) error = "Empty ERROR was passed, so no informations available"; error = "Empty ERROR was passed, so no informations available";
if (typeof error === "string") { if (typeof error === "string") {
let e = new Error("This is a fake error, to get a stack trace"); let e = new Error("This is a fake error, to get a stack trace");
this.message(LoggingTypes.Error, [error, "\n", e.stack]); this.message(LoggingTypes.Error, [error, "\n", e.stack]);
} else { } else {
this.message(LoggingTypes.Error, [error.message, "\n", error.stack], getCallerFromExisting(error)); this.message(
LoggingTypes.Error,
[error.message, "\n", error.stack],
getCallerFromExisting(error)
);
} }
} }
@ -157,8 +180,61 @@ export class LoggingBase {
this.message(LoggingTypes.Error, message); this.message(LoggingTypes.Error, message);
} }
private message(type: LoggingTypes, message: any[], caller?: { file: string, line: number }) { protected getCurrentTime() {
let date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''); if (browser && window.performance && window.performance.now) {
return window.performance.now();
} else {
return Date.now();
}
}
/**
* The time difference in milliseconds (fractions allowed!)
* @param start Start time from getCurrentTime
*/
protected getTimeDiff(start: any) {
if (browser && window.performance && window.performance.now) {
return window.performance.now() - start;
} else {
return Date.now() - start;
}
}
time(id?: string, name = id) {
if (!id) {
id = Math.floor(Math.random() * 899999 + 100000).toString();
}
this.timerMap.set(id, {
name,
start: this.getCurrentTime(),
});
return {
id,
end: () => this.timeEnd(id),
};
}
timeEnd(id: string) {
let timer = this.timerMap.get(id);
if (timer) {
let diff = this.getTimeDiff(timer.start);
Logging.message(LoggingTypes.Debug, [
withColor(Colors.GREEN, `[${timer.name}]`),
`->`,
withColor(Colors.BLUE, diff.toFixed(4)),
"ms",
]);
}
}
private message(
type: LoggingTypes,
message: any[],
caller?: { file: string; line: number }
) {
let date = new Date().toISOString().replace(/T/, " ").replace(/\..+/, "");
let file_raw = caller || getCallerFile(); let file_raw = caller || getCallerFile();
let file = `${file_raw.file}:${String(file_raw.line).padEnd(3, " ")}`; let file = `${file_raw.file}:${String(file_raw.line).padEnd(3, " ")}`;
@ -184,9 +260,9 @@ export class LoggingBase {
const a = (text: string, formats: Format[] = []) => { const a = (text: string, formats: Format[] = []) => {
prefix.push({ prefix.push({
text, text,
formats formats,
}) });
} };
a("["); a("[");
a(date, this._formatMap.date); a(date, this._formatMap.date);
@ -198,7 +274,6 @@ export class LoggingBase {
} }
a("]: "); a("]: ");
let raw: string[] = []; let raw: string[] = [];
const formatted: FormattedLine[] = []; const formatted: FormattedLine[] = [];
@ -206,10 +281,10 @@ export class LoggingBase {
const newLine = () => { const newLine = () => {
if (line && line.length > 0) { if (line && line.length > 0) {
formatted.push(line); formatted.push(line);
raw.push(line.map(e => e.text).join("")); raw.push(line.map((e) => e.text).join(""));
} }
line = [...prefix]; line = [...prefix];
} };
newLine(); newLine();
message.forEach((e, i) => { message.forEach((e, i) => {
@ -219,21 +294,27 @@ export class LoggingBase {
if (e[colorSymbol]) { if (e[colorSymbol]) {
formats.push({ formats.push({
type: FormatTypes.COLOR, type: FormatTypes.COLOR,
color: e[colorSymbol] color: e[colorSymbol],
}) });
e = e.value; e = e.value;
} }
} }
if (typeof e !== "string") if (typeof e !== "string")
e = inspect(e, { colors: true, showHidden: true, depth: 3 }) as string; e = inspect(e, {
colors: true,
showHidden: true,
depth: 3,
}) as string;
} }
removeColors(e).split("\n").map((text, index, { length }) => { removeColors(e)
.split("\n")
.map((text, index, { length }) => {
line.push({ text, formats }); line.push({ text, formats });
if (index < length - 1) { if (index < length - 1) {
newLine(); newLine();
} }
}) });
if (!e.endsWith("\n") && i < message.length - 1) { if (!e.endsWith("\n") && i < message.length - 1) {
line.push({ text: " ", formats: [] }); line.push({ text: " ", formats: [] });
@ -248,10 +329,10 @@ export class LoggingBase {
name: this._name, name: this._name,
text: { text: {
raw, raw,
formatted formatted,
}, },
type type,
} };
this.messageObservable.send(msg); this.messageObservable.send(msg);
} }
@ -267,8 +348,8 @@ export interface ColorFormat {
export function withColor(color: Colors, value: any): ColorFormat { export function withColor(color: Colors, value: any): ColorFormat {
return { return {
[colorSymbol]: color, [colorSymbol]: color,
value value,
} };
} }
function getStack() { function getStack() {
@ -277,8 +358,8 @@ function getStack() {
// Override with function that just returns `stack` // Override with function that just returns `stack`
(<any>Error).prepareStackTrace = function (_, stack) { (<any>Error).prepareStackTrace = function (_, stack) {
return stack return stack;
} };
// Create a new `Error`, which automatically gets `stack` // Create a new `Error`, which automatically gets `stack`
let err = new Error(); let err = new Error();
@ -292,7 +373,7 @@ function getStack() {
// Remove superfluous function call on stack // Remove superfluous function call on stack
stack.shift(); // getStack --> Error stack.shift(); // getStack --> Error
return stack return stack;
} }
function baseName(path) { function baseName(path) {
@ -301,7 +382,7 @@ function baseName(path) {
function getCallerFile() { function getCallerFile() {
try { try {
let stack = getStack() let stack = getStack();
let current_file = stack.shift().getFileName(); let current_file = stack.shift().getFileName();
@ -310,24 +391,25 @@ function getCallerFile() {
if (current_file !== caller_file.getFileName()) if (current_file !== caller_file.getFileName())
return { return {
file: baseName(caller_file.getFileName()), file: baseName(caller_file.getFileName()),
line: caller_file.getLineNumber() line: caller_file.getLineNumber(),
}; };
} }
} catch (err) {} } catch (err) {}
return { file: undefined, line: 0 }; return { file: undefined, line: 0 };
} }
function getCallerFromExisting(err: Error): { file: string, line: number } { function getCallerFromExisting(err: Error): { file: string; line: number } {
if (!err || !err.stack) return { file: "NOFILE", line: 0 }; if (!err || !err.stack) return { file: "NOFILE", line: 0 };
let lines = err.stack.split("\n"); let lines = err.stack.split("\n");
lines.shift(); // removing first line lines.shift(); // removing first line
while (lines.length > 0) { while (lines.length > 0) {
let line = lines.shift(); let line = lines.shift();
let matches = line.match(/[a-zA-Z_-]+[.][a-zA-Z_-]+[:][0-9]+/g) let matches = line.match(/[a-zA-Z_-]+[.][a-zA-Z_-]+[:][0-9]+/g);
if (matches && matches.length > 0) { if (matches && matches.length > 0) {
let [f, line] = matches[0].split(":") let [f, line] = matches[0].split(":");
return { return {
file: f, line: Number(line) file: f,
line: Number(line),
}; };
} }
} }

View File

@ -1,6 +1,12 @@
import { ObservableInterface } from "@hibas123/utils"; import { ObservableInterface } from "@hibas123/utils";
import { Colors } from "./index"; import { Colors } from "./index";
import { Adapter, Message, FormattedLine, TerminalFormats, FormatTypes } from "./types"; import {
Adapter,
Message,
FormattedLine,
TerminalFormats,
FormatTypes,
} from "./types";
const browser = typeof window !== "undefined"; const browser = typeof window !== "undefined";
@ -128,20 +134,21 @@ export class ConsoleAdapter implements Adapter {
if (message.name) prefix = `[${message.name}]=>`; if (message.name) prefix = `[${message.name}]=>`;
if (browser) { if (browser) {
let formats: string[] = []; let formats: string[] = [];
let text = lines.map(line => { let text = lines
.map((line) => {
let [t, fmts] = this.formatLine(line); let [t, fmts] = this.formatLine(line);
formats.push(...fmts); formats.push(...fmts);
return t; return t;
}).join("\n"); })
.join("\n");
// console.log(formats); // console.log(formats);
console.log(text, ...formats); console.log(text, ...formats);
} else { } else {
lines.forEach(line => { lines.forEach((line) => {
let [text] = this.formatLine(line); let [text] = this.formatLine(line);
console.log(prefix + text); console.log(prefix + text);
}) });
} }
} }
} }

View File

@ -1,39 +1,44 @@
import { Logging, LoggingBase, LoggingTypes, Colors, withColor } from "."; import { Logging, LoggingBase, LoggingTypes, Colors, withColor } from ".";
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"
);
Logging.log(withColor(Colors.MAGENTA, "This text should be magenta!"), "This not!") Logging.log(
Logging.log(withColor(Colors.MAGENTA, { somekey: "Some value" })) withColor(Colors.MAGENTA, "This text should be magenta!"),
"This not!"
);
Logging.log(withColor(Colors.MAGENTA, { somekey: "Some value" }));
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");
cus.log("This has some %c symbols inside of it!"); cus.log("This has some %c symbols inside of it!");
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");
Logging.debug("Only Errors should appear:") Logging.debug("Only Errors should appear:");
Logging.logLevel = LoggingTypes.Error; Logging.logLevel = LoggingTypes.Error;
Logging.debug("This should not be there 1"); Logging.debug("This should not be there 1");
@ -43,3 +48,6 @@ Logging.warning("This should not be there 4");
Logging.error("This should be there 1"); Logging.error("This should be there 1");
Logging.errorMessage("This should be there 2"); Logging.errorMessage("This should be there 2");
const timer = Logging.time("timer1", "Test Timer");
setTimeout(() => timer.end(), 1000);