update
This commit is contained in:
parent
5215ba24cc
commit
6c3759c70d
12 changed files with 47 additions and 15 deletions
9
src/ILogItem.ts
Normal file
9
src/ILogItem.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { LogLevel } from "./LogLevel";
|
||||
|
||||
export interface ILogItem {
|
||||
timestamp: Date;
|
||||
origin?: string;
|
||||
flags?: string[];
|
||||
level: LogLevel;
|
||||
data: any;
|
||||
}
|
8
src/LogLevel.ts
Normal file
8
src/LogLevel.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Critical
|
||||
}
|
26
src/common.ts
Normal file
26
src/common.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Dispatch } from "./dispatch/Dispatch";
|
||||
import { ILogItem } from "./ILogItem";
|
||||
import { LogLevel } from "./LogLevel";
|
||||
|
||||
let CURRENT_DISPATCH: Dispatch = new class extends Dispatch {
|
||||
output(item: ILogItem): void {
|
||||
process.stdout.write(`level{${item.level}} timestamp{${item.timestamp}} origin{${item.origin}} flags{${item.flags}} data{${item.data}}\n`)
|
||||
}
|
||||
};
|
||||
|
||||
export const setDispatch = (dispatch: Dispatch) => CURRENT_DISPATCH = dispatch;
|
||||
|
||||
export const LOGT = (...args) => CURRENT_DISPATCH.process({ timestamp: new Date(), level: LogLevel.Trace, data: args });
|
||||
export const LOGD = (...args) => CURRENT_DISPATCH.process({ timestamp: new Date(), level: LogLevel.Debug, data: args });
|
||||
export const LOGI = (...args) => CURRENT_DISPATCH.process({ timestamp: new Date(), level: LogLevel.Info, data: args });
|
||||
export const LOGW = (...args) => CURRENT_DISPATCH.process({ timestamp: new Date(), level: LogLevel.Warn, data: args });
|
||||
export const LOGE = (...args) => CURRENT_DISPATCH.process({ timestamp: new Date(), level: LogLevel.Error, data: args });
|
||||
export const LOGC = (...args) => CURRENT_DISPATCH.process({ timestamp: new Date(), level: LogLevel.Critical, data: args });
|
||||
|
||||
export const LOG = (item: Partial<ILogItem>) => CURRENT_DISPATCH.process({
|
||||
flags: item.flags ?? [],
|
||||
origin: item.origin ?? "unknown",
|
||||
level: item.level ?? LogLevel.Trace,
|
||||
timestamp: item.timestamp ?? new Date(),
|
||||
data: item.data ?? ""
|
||||
})
|
24
src/dispatch/Dispatch.ts
Normal file
24
src/dispatch/Dispatch.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { IDispatch, IDispatchConfig } from "./IDispatch";
|
||||
import { ILogItem } from "../ILogItem";
|
||||
import { LogLevel } from "../LogLevel";
|
||||
|
||||
export abstract class Dispatch implements IDispatch {
|
||||
children: IDispatch[] = [];
|
||||
|
||||
constructor(config: IDispatchConfig = {}) { }
|
||||
|
||||
process(item: ILogItem): void {
|
||||
this.output(item);
|
||||
}
|
||||
|
||||
output(item: ILogItem): void {
|
||||
for (const dispatch of this.children) {
|
||||
dispatch.process(item);
|
||||
}
|
||||
}
|
||||
|
||||
chain(dispatch: IDispatch): Dispatch {
|
||||
this.children.push(dispatch);
|
||||
return this;
|
||||
}
|
||||
}
|
21
src/dispatch/FileDispatch.ts
Normal file
21
src/dispatch/FileDispatch.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as fs from "fs";
|
||||
import { FormattedDispatch } from "./FormattedDispatch";
|
||||
import { App } from "../App";
|
||||
import { ILogItem } from "../ILogItem";
|
||||
import { LOGE, LOGW } from "../common";
|
||||
|
||||
export class FileDispatch extends FormattedDispatch {
|
||||
path: string;
|
||||
stream: fs.WriteStream;
|
||||
|
||||
constructor(path: string) {
|
||||
super();
|
||||
this.path = path;
|
||||
this.stream = fs.createWriteStream(path, { flags: "a" });
|
||||
this.stream.on("error", (err) => LOGE(`Error writing to file: ${this.path}`, err));
|
||||
}
|
||||
|
||||
output(item: ILogItem): void {
|
||||
this.stream.write(item.data + "\n");
|
||||
}
|
||||
}
|
51
src/dispatch/FormattedDispatch.ts
Normal file
51
src/dispatch/FormattedDispatch.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { object } from "zod";
|
||||
import { Dispatch } from "./Dispatch";
|
||||
import { ILogItem } from "../ILogItem";
|
||||
import { LogLevel } from "../LogLevel";
|
||||
|
||||
export class FormattedDispatch extends Dispatch {
|
||||
private formatString: string = "%t %l <%o>: %m";
|
||||
private levelFixedLength = Object.keys(LogLevel).reduce((acc, cur) => cur.length > acc ? cur.length : acc, 0)
|
||||
|
||||
private fmtPart(x: any) {
|
||||
switch (typeof x) {
|
||||
case "string":
|
||||
case "number":
|
||||
return x;
|
||||
case "object":
|
||||
return JSON.stringify(x, null, 2);
|
||||
default:
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
format(item: ILogItem): string {
|
||||
let data;
|
||||
|
||||
// Array to string
|
||||
if (Array.isArray(item.data))
|
||||
data = item.data
|
||||
.map((item: any) => this.fmtPart(item))
|
||||
.join(" ");
|
||||
else data = this.fmtPart(item.data);
|
||||
|
||||
|
||||
return this.formatString
|
||||
.replace("%t", item.timestamp.toISOString())
|
||||
.replace("%l", LogLevel[item.level].padEnd(this.levelFixedLength))
|
||||
.replace("%o", item.origin || "")
|
||||
.replace(
|
||||
"%m",
|
||||
data);
|
||||
}
|
||||
|
||||
process(item: ILogItem): void {
|
||||
if (item.flags?.includes("formatting:none")) return super.process(item);
|
||||
else if (item.flags?.includes("formatting:json_string")) return super.process({...item, data: JSON.stringify(item.data)});
|
||||
else if (item.flags?.includes("formatting:pretty_json_string")) return super.process({...item, data: JSON.stringify(item.data, null, 2)});
|
||||
super.process({
|
||||
...item,
|
||||
data: this.format(item),
|
||||
});
|
||||
}
|
||||
}
|
9
src/dispatch/IDispatch.ts
Normal file
9
src/dispatch/IDispatch.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { ILogItem } from "../ILogItem";
|
||||
|
||||
export interface IDispatchConfig {}
|
||||
|
||||
export interface IDispatch {
|
||||
chain(dispatch: IDispatch): void; // add dispatches
|
||||
process(item: ILogItem): void; // process a line; format, stats, etc.
|
||||
output(item: ILogItem): void; // output a line
|
||||
}
|
54
src/dispatch/SmartDispatch.ts
Normal file
54
src/dispatch/SmartDispatch.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { statSync } from "fs";
|
||||
import { Dispatch } from "./Dispatch";
|
||||
import { ILogItem } from "../ILogItem";
|
||||
import { LogLevel } from "../LogLevel";
|
||||
|
||||
export class SmartDispatch extends Dispatch {
|
||||
process(item: ILogItem) {
|
||||
|
||||
let origin = item.origin;
|
||||
if (!item.origin) {
|
||||
let err = new Error();
|
||||
// we need to look through the stack and keep track of intermediate dispatches
|
||||
let stack = err.stack!.split("\n");
|
||||
let i = 0;
|
||||
while (!stack[i].includes("at SmartDispatch.process")) i++;
|
||||
|
||||
let src = stack[i + 2].trim();
|
||||
let path: string | null = null;
|
||||
|
||||
// now we need to format the src (at ...:..:.. or at App.<anonymous> ..., etc)
|
||||
if (src.includes("at ")) src = src.split("at ")[1];
|
||||
if (src.includes(" (")) src = src.split(" (")[1];
|
||||
if (src.includes(")")) src = src.split(")")[0];
|
||||
|
||||
let file = {
|
||||
path: src.split(":")[0],
|
||||
line: src.split(":")[1],
|
||||
column: src.split(":")[2],
|
||||
};
|
||||
|
||||
try {
|
||||
path = statSync(file.path).isFile() ? file.path : null;
|
||||
} catch (e) {
|
||||
path = null;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
// trim the path to the project root
|
||||
if (path.startsWith(process.cwd()))
|
||||
path = "." + path.slice(process.cwd().length);
|
||||
src = `${path}:${file.line}:${file.column}`;
|
||||
}
|
||||
origin = src;
|
||||
}
|
||||
|
||||
if (!item.level) item.level = LogLevel.Trace;
|
||||
if (!item.timestamp) item.timestamp = new Date();
|
||||
|
||||
super.process({
|
||||
...item,
|
||||
origin
|
||||
});
|
||||
}
|
||||
}
|
10
src/dispatch/StdoutDispatch.ts
Normal file
10
src/dispatch/StdoutDispatch.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { FormattedDispatch } from "./FormattedDispatch";
|
||||
import { IDispatch } from "./IDispatch";
|
||||
import { ILogItem } from "../ILogItem";
|
||||
import { LogLevel } from "../LogLevel";
|
||||
|
||||
export class StdoutDispatch extends FormattedDispatch {
|
||||
output(item: ILogItem) {
|
||||
process.stdout.write(item.data + "\n");
|
||||
}
|
||||
}
|
17
src/index.ts
Normal file
17
src/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
export {
|
||||
LOG,
|
||||
LOGT,
|
||||
LOGD,
|
||||
LOGI,
|
||||
LOGW,
|
||||
LOGE,
|
||||
LOGC,
|
||||
setDispatch
|
||||
} from "./common";
|
||||
|
||||
export { Dispatch } from './dispatch/Dispatch';
|
||||
export { IDispatch } from './dispatch/IDispatch';
|
||||
export { FormattedDispatch } from './dispatch/FormattedDispatch';
|
||||
export { SmartDispatch } from './dispatch/SmartDispatch';
|
||||
export { FileDispatch } from './dispatch/FileDispatch';
|
||||
export { StdoutDispatch } from './dispatch/StdoutDispatch';
|
Loading…
Add table
Add a link
Reference in a new issue