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
indent_style = space
indent_size = 3

View File

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

View File

@ -1,334 +1,416 @@
import { Observable } from "@hibas123/utils";
import { ConsoleAdapter } from "./consolewriter";
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) {
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");
// while (index >= 0) {
// text = text.substring(0, index) + text.substring(index + 5, text.length);
// index = text.indexOf("\x1b");
// }
return text;
// let index = text.indexOf("\x1b");
// while (index >= 0) {
// text = text.substring(0, index) + text.substring(index + 5, text.length);
// index = text.indexOf("\x1b");
// }
return text;
}
export interface LoggingBaseOptions {
/**
* Name will be prefixed on Console output and added to logfiles, if not specified here
*/
name: string,
/**
* Prints output to console
*/
console: boolean;
/**
* Name will be prefixed on Console output and added to logfiles, if not specified here
*/
name: string;
/**
* Prints output to console
*/
console: boolean;
}
export class LoggingBase {
private _formatMap: FormatConfig = new DefaultFormatConfig();
private _formatMap: FormatConfig = new DefaultFormatConfig();
public set formatMap(value: FormatConfig) {
this._formatMap = value;
}
public set formatMap(value: FormatConfig) {
this._formatMap = value;
}
private adapter = new Set<Adapter>();
private adapter_init: Promise<void>[] = [];
private timerMap = new Map<string, { name: string; start: any }>();
private adapter = new Set<Adapter>();
private adapter_init: Promise<void>[] = [];
private messageObservable = new Observable<Message>();
protected _name: string;
private messageObservable = new Observable<Message>();
protected _name: string;
private _logLevel = LoggingTypes.Debug;
get logLevel() {
return this._logLevel;
}
private _logLevel = LoggingTypes.Debug;
set logLevel(value: LoggingTypes) {
this._logLevel = value;
}
get logLevel() {
return this._logLevel;
}
get name() {
return this._name;
}
set logLevel(value: LoggingTypes) {
this._logLevel = value;
}
constructor(options?: Partial<LoggingBaseOptions> | string) {
let opt: Partial<LoggingBaseOptions>;
if (!options) opt = {};
else if (typeof options === "string") {
opt = { name: options };
} else {
opt = options;
}
get name() {
return this._name;
}
let config: LoggingBaseOptions = {
name: undefined,
console: true,
...opt,
};
constructor(options?: Partial<LoggingBaseOptions> | string) {
let opt: Partial<LoggingBaseOptions>;
if (!options) opt = {}
else if (typeof options === "string") {
opt = { name: options };
} else {
opt = options;
}
if (config.name) this._name = config.name;
let config: LoggingBaseOptions = {
name: undefined,
console: true,
...opt
};
for (let key in this) {
if (typeof this[key] === "function")
this[key] = (<any>this[key]).bind(this);
}
if (config.name)
this._name = config.name;
if (config.console) {
this.addAdapter(new ConsoleAdapter());
}
for (let key in this) {
if (typeof this[key] === "function") this[key] = (<any>this[key]).bind(this);
}
//Binding function to this
this.debug = this.debug.bind(this);
this.log = this.log.bind(this);
this.warn = this.warn.bind(this);
this.warning = this.warning.bind(this);
this.error = this.error.bind(this);
this.errorMessage = this.errorMessage.bind(this);
this.flush = this.flush.bind(this);
}
if (config.console) {
this.addAdapter(new ConsoleAdapter());
}
addAdapter(adapter: Adapter) {
if (!this.adapter.has(adapter)) {
this.adapter.add(adapter);
let prms = Promise.resolve(
adapter.init(this.messageObservable.getPublicApi(), this._name)
);
this.adapter_init.push(prms);
}
}
flush(sync: true): void;
flush(sync: false): Promise<void>;
flush(sync: boolean): void | Promise<void> {
if (sync) {
this.adapter.forEach((elm) => elm.flush(true));
} else {
let adapters: (void | Promise<void>)[] = [];
this.adapter.forEach((elm) => adapters.push(elm.flush(false)));
return Promise.all(adapters).then(() => {});
}
}
//Binding function to this
this.debug = this.debug.bind(this);
this.log = this.log.bind(this);
this.warn = this.warn.bind(this);
this.warning = this.warning.bind(this);
this.error = this.error.bind(this);
this.errorMessage = this.errorMessage.bind(this);
this.flush = this.flush.bind(this);
}
public close() {
this.adapter.forEach((adapter) =>
adapter.close ? adapter.close() : undefined
);
}
addAdapter(adapter: Adapter) {
if (!this.adapter.has(adapter)) {
this.adapter.add(adapter);
let prms = Promise.resolve(adapter.init(this.messageObservable.getPublicApi(), this._name));
this.adapter_init.push(prms);
}
}
public waitForSetup() {
return Promise.all(this.adapter_init);
}
flush(sync: true): void;
flush(sync: false): Promise<void>;
flush(sync: boolean): void | Promise<void> {
if (sync) {
this.adapter.forEach(elm => elm.flush(true));
} else {
let adapters: (void | Promise<void>)[] = [];
this.adapter.forEach(elm => adapters.push(elm.flush(false)));
return Promise.all(adapters).then(() => { });
}
}
debug(...message: any[]) {
if (this._logLevel <= LoggingTypes.Debug)
this.message(LoggingTypes.Debug, message);
}
public close() {
this.adapter.forEach(adapter => adapter.close ? adapter.close() : undefined);
}
log(...message: any[]) {
if (this._logLevel <= LoggingTypes.Log)
this.message(LoggingTypes.Log, message);
}
public waitForSetup() {
return Promise.all(this.adapter_init);
}
warning(...message: any[]) {
if (this._logLevel <= LoggingTypes.Warning)
this.message(LoggingTypes.Warning, message);
}
debug(...message: any[]) {
if (this._logLevel <= LoggingTypes.Debug)
this.message(LoggingTypes.Debug, message);
}
warn(...message: any[]) {
if (this._logLevel <= LoggingTypes.Warning)
this.message(LoggingTypes.Warning, message);
}
log(...message: any[]) {
if (this._logLevel <= LoggingTypes.Log)
this.message(LoggingTypes.Log, message);
}
error(error: Error | string) {
if (this._logLevel > LoggingTypes.Error) return;
if (!error)
error = "Empty ERROR was passed, so no informations available";
if (typeof error === "string") {
let e = new Error("This is a fake error, to get a stack trace");
this.message(LoggingTypes.Error, [error, "\n", e.stack]);
} else {
this.message(
LoggingTypes.Error,
[error.message, "\n", error.stack],
getCallerFromExisting(error)
);
}
}
warning(...message: any[]) {
if (this._logLevel <= LoggingTypes.Warning)
this.message(LoggingTypes.Warning, message);
}
errorMessage(...message: any[]) {
if (this._logLevel <= LoggingTypes.Error)
this.message(LoggingTypes.Error, message);
}
warn(...message: any[]) {
if (this._logLevel <= LoggingTypes.Warning)
this.message(LoggingTypes.Warning, message);
}
protected getCurrentTime() {
if (browser && window.performance && window.performance.now) {
return window.performance.now();
} else {
return Date.now();
}
}
error(error: Error | string) {
if (this._logLevel > LoggingTypes.Error)
return;
if (!error) error = "Empty ERROR was passed, so no informations available";
if (typeof error === "string") {
let e = new Error("This is a fake error, to get a stack trace");
this.message(LoggingTypes.Error, [error, "\n", e.stack]);
} else {
this.message(LoggingTypes.Error, [error.message, "\n", error.stack], getCallerFromExisting(error));
}
}
/**
* 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;
}
}
errorMessage(...message: any[]) {
if (this._logLevel <= LoggingTypes.Error)
this.message(LoggingTypes.Error, message);
}
time(id?: string, name = id) {
if (!id) {
id = Math.floor(Math.random() * 899999 + 100000).toString();
}
private message(type: LoggingTypes, message: any[], caller?: { file: string, line: number }) {
let date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
this.timerMap.set(id, {
name,
start: this.getCurrentTime(),
});
let file_raw = caller || getCallerFile();
let file = `${file_raw.file}:${String(file_raw.line).padEnd(3, " ")}`;
return {
id,
end: () => this.timeEnd(id),
};
}
let type_str = LoggingTypes[type].toUpperCase().padEnd(5, " ");
let type_format: Format[] = [];
switch (type) {
case LoggingTypes.Log:
type_format = this._formatMap.log;
break;
case LoggingTypes.Error:
type_format = this._formatMap.error;
break;
case LoggingTypes.Debug:
type_format = this._formatMap.debug;
break;
case LoggingTypes.Warning:
type_format = this._formatMap.warning;
break;
}
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",
]);
}
}
const prefix: FormattedText[] = [];
const a = (text: string, formats: Format[] = []) => {
prefix.push({
text,
formats
})
}
private message(
type: LoggingTypes,
message: any[],
caller?: { file: string; line: number }
) {
let date = new Date().toISOString().replace(/T/, " ").replace(/\..+/, "");
a("[");
a(date, this._formatMap.date);
a("][");
a(type_str, type_format);
if (file_raw.file) {
a("][");
a(file, this._formatMap.file);
}
a("]: ");
let file_raw = caller || getCallerFile();
let file = `${file_raw.file}:${String(file_raw.line).padEnd(3, " ")}`;
let type_str = LoggingTypes[type].toUpperCase().padEnd(5, " ");
let type_format: Format[] = [];
switch (type) {
case LoggingTypes.Log:
type_format = this._formatMap.log;
break;
case LoggingTypes.Error:
type_format = this._formatMap.error;
break;
case LoggingTypes.Debug:
type_format = this._formatMap.debug;
break;
case LoggingTypes.Warning:
type_format = this._formatMap.warning;
break;
}
let raw: string[] = [];
const prefix: FormattedText[] = [];
const a = (text: string, formats: Format[] = []) => {
prefix.push({
text,
formats,
});
};
const formatted: FormattedLine[] = [];
let line: FormattedLine;
const newLine = () => {
if (line && line.length > 0) {
formatted.push(line);
raw.push(line.map(e => e.text).join(""));
a("[");
a(date, this._formatMap.date);
a("][");
a(type_str, type_format);
if (file_raw.file) {
a("][");
a(file, this._formatMap.file);
}
a("]: ");
let raw: string[] = [];
const formatted: FormattedLine[] = [];
let line: FormattedLine;
const newLine = () => {
if (line && line.length > 0) {
formatted.push(line);
raw.push(line.map((e) => e.text).join(""));
}
line = [...prefix];
};
newLine();
message.forEach((e, i) => {
let formats: Format[] = [];
if (typeof e !== "string") {
if (e && typeof e === "object") {
if (e[colorSymbol]) {
formats.push({
type: FormatTypes.COLOR,
color: e[colorSymbol],
});
e = e.value;
}
}
line = [...prefix];
}
newLine();
if (typeof e !== "string")
e = inspect(e, {
colors: true,
showHidden: true,
depth: 3,
}) as string;
}
message.forEach((e, i) => {
let formats: Format[] = [];
if (typeof e !== "string") {
if (e && typeof e === "object") {
if (e[colorSymbol]) {
formats.push({
type: FormatTypes.COLOR,
color: e[colorSymbol]
})
e = e.value;
}
}
if (typeof e !== "string")
e = inspect(e, { colors: true, showHidden: true, depth: 3 }) as string;
}
removeColors(e)
.split("\n")
.map((text, index, { length }) => {
line.push({ text, formats });
if (index < length - 1) {
newLine();
}
});
removeColors(e).split("\n").map((text, index, { length }) => {
line.push({ text, formats });
if (index < length - 1) {
newLine();
}
})
if (!e.endsWith("\n") && i < message.length - 1) {
line.push({ text: " ", formats: [] });
}
});
if (!e.endsWith("\n") && i < message.length - 1) {
line.push({ text: " ", formats: [] });
}
});
newLine();
newLine();
let msg: Message = {
date: new Date(),
file,
name: this._name,
text: {
raw,
formatted,
},
type,
};
let msg: Message = {
date: new Date(),
file,
name: this._name,
text: {
raw,
formatted
},
type
}
this.messageObservable.send(msg);
}
this.messageObservable.send(msg);
}
}
const colorSymbol = Symbol("color");
export interface ColorFormat {
[colorSymbol]: Colors;
value: any;
[colorSymbol]: Colors;
value: any;
}
export function withColor(color: Colors, value: any): ColorFormat {
return {
[colorSymbol]: color,
value
}
return {
[colorSymbol]: color,
value,
};
}
function getStack() {
// Save original Error.prepareStackTrace
let origPrepareStackTrace = (<any>Error).prepareStackTrace;
// Save original Error.prepareStackTrace
let origPrepareStackTrace = (<any>Error).prepareStackTrace;
// Override with function that just returns `stack`
(<any>Error).prepareStackTrace = function (_, stack) {
return stack
}
// Override with function that just returns `stack`
(<any>Error).prepareStackTrace = function (_, stack) {
return stack;
};
// Create a new `Error`, which automatically gets `stack`
let err = new Error();
// Create a new `Error`, which automatically gets `stack`
let err = new Error();
// Evaluate `err.stack`, which calls our new `Error.prepareStackTrace`
let stack: any[] = <any>err.stack;
// Evaluate `err.stack`, which calls our new `Error.prepareStackTrace`
let stack: any[] = <any>err.stack;
// Restore original `Error.prepareStackTrace`
(<any>Error).prepareStackTrace = origPrepareStackTrace;
// Restore original `Error.prepareStackTrace`
(<any>Error).prepareStackTrace = origPrepareStackTrace;
// Remove superfluous function call on stack
stack.shift(); // getStack --> Error
// Remove superfluous function call on stack
stack.shift(); // getStack --> Error
return stack
return stack;
}
function baseName(path) {
return path.split(/[\\/]/).pop();
return path.split(/[\\/]/).pop();
}
function getCallerFile() {
try {
let stack = getStack()
try {
let stack = getStack();
let current_file = stack.shift().getFileName();
let current_file = stack.shift().getFileName();
while (stack.length) {
let caller_file = stack.shift();
if (current_file !== caller_file.getFileName())
return {
file: baseName(caller_file.getFileName()),
line: caller_file.getLineNumber()
};
}
} catch (err) { }
return { file: undefined, line: 0 };
}
function getCallerFromExisting(err: Error): { file: string, line: number } {
if (!err || !err.stack) return { file: "NOFILE", line: 0 };
let lines = err.stack.split("\n");
lines.shift();// removing first line
while (lines.length > 0) {
let line = lines.shift();
let matches = line.match(/[a-zA-Z_-]+[.][a-zA-Z_-]+[:][0-9]+/g)
if (matches && matches.length > 0) {
let [f, line] = matches[0].split(":")
while (stack.length) {
let caller_file = stack.shift();
if (current_file !== caller_file.getFileName())
return {
file: f, line: Number(line)
file: baseName(caller_file.getFileName()),
line: caller_file.getLineNumber(),
};
}
}
}
} catch (err) {}
return { file: undefined, line: 0 };
}
function getCallerFromExisting(err: Error): { file: string; line: number } {
if (!err || !err.stack) return { file: "NOFILE", line: 0 };
let lines = err.stack.split("\n");
lines.shift(); // removing first line
while (lines.length > 0) {
let line = lines.shift();
let matches = line.match(/[a-zA-Z_-]+[.][a-zA-Z_-]+[:][0-9]+/g);
if (matches && matches.length > 0) {
let [f, line] = matches[0].split(":");
return {
file: f,
line: Number(line),
};
}
}
}

View File

@ -1,6 +1,12 @@
import { ObservableInterface } from "@hibas123/utils";
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";
@ -9,7 +15,7 @@ export class ConsoleAdapter implements Adapter {
observable.subscribe(this.onMessage.bind(this));
}
flush() { }
flush() {}
// TODO: Check if required!
// private escape(text: string): string {
@ -128,20 +134,21 @@ export class ConsoleAdapter implements Adapter {
if (message.name) prefix = `[${message.name}]=>`;
if (browser) {
let formats: string[] = [];
let text = lines.map(line => {
let [t, fmts] = this.formatLine(line);
formats.push(...fmts);
return t;
}).join("\n");
let text = lines
.map((line) => {
let [t, fmts] = this.formatLine(line);
formats.push(...fmts);
return t;
})
.join("\n");
// console.log(formats);
console.log(text, ...formats);
} else {
lines.forEach(line => {
lines.forEach((line) => {
let [text] = this.formatLine(line);
console.log(prefix + text);
})
});
}
}
}

View File

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