all repos — kitten @ e8d6d7fac30bb733d714a36350d2a3edcfd2e41b

pkgs/router/src/logger.ts (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";

export interface LogEntry {
	level: LogLevel;
	msg: string;
	time: number;
	[key: string]: unknown;
}

export interface LoggerOptions {
	level?: LogLevel;
	name?: string;
	base?: Record<string, unknown>;
	transport?: (entry: LogEntry) => void;
}

export interface KittenLogger {
	trace(msg: string, data?: Record<string, unknown>): void;
	debug(msg: string, data?: Record<string, unknown>): void;
	info(msg: string, data?: Record<string, unknown>): void;
	warn(msg: string, data?: Record<string, unknown>): void;
	error(msg: string, data?: Record<string, unknown>): void;
	fatal(msg: string, data?: Record<string, unknown>): void;
	child(bindings: Record<string, unknown>): KittenLogger;
}

const LEVELS: Record<LogLevel, number> = {
	trace: 10,
	debug: 20,
	info: 30,
	warn: 40,
	error: 50,
	fatal: 60,
};

const defaultTransport = (entry: LogEntry) => {
	const out = JSON.stringify(entry);
	if (entry.level === "error" || entry.level === "fatal") console.error(out);
	else console.log(out);
};

export class Logger implements KittenLogger {
	private readonly minLevel: number;
	private readonly base: Record<string, unknown>;
	private readonly transport: (entry: LogEntry) => void;

	constructor(private readonly options: LoggerOptions = {}) {
		this.minLevel = LEVELS[options.level ?? "info"];
		this.base = {
			...(options.name ? { name: options.name } : {}),
			...options.base,
		};
		this.transport = options.transport ?? defaultTransport;
	}

	private write(level: LogLevel, msg: string, data?: Record<string, unknown>) {
		if (LEVELS[level] < this.minLevel) return;
		this.transport({
			level,
			msg,
			time: Date.now(),
			...this.base,
			...data,
		});
	}

	trace(msg: string, data?: Record<string, unknown>) {
		this.write("trace", msg, data);
	}
	debug(msg: string, data?: Record<string, unknown>) {
		this.write("debug", msg, data);
	}
	info(msg: string, data?: Record<string, unknown>) {
		this.write("info", msg, data);
	}
	warn(msg: string, data?: Record<string, unknown>) {
		this.write("warn", msg, data);
	}
	error(msg: string, data?: Record<string, unknown>) {
		this.write("error", msg, data);
	}
	fatal(msg: string, data?: Record<string, unknown>) {
		this.write("fatal", msg, data);
	}

	child(bindings: Record<string, unknown>): KittenLogger {
		return new Logger({
			...this.options,
			base: { ...this.base, ...bindings },
		});
	}
}

const baseLogger = new Logger();
export { baseLogger };