std/log/logger.ts

225 lines
6.2 KiB
TypeScript
Raw Normal View History

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { getLevelByName, getLevelName, LogLevels } from "./levels.ts";
import type { LevelName, LogLevel } from "./levels.ts";
import type { BaseHandler } from "./base_handler.ts";
2019-01-02 14:12:48 +00:00
// deno-lint-ignore no-explicit-any
export type GenericFunction = (...args: any[]) => any;
export interface LogRecordOptions {
msg: string;
args: unknown[];
level: LogLevel;
loggerName: string;
}
export class LoggerConfig {
level?: LevelName;
handlers?: string[];
}
export interface LogConfig {
handlers?: {
[name: string]: BaseHandler;
};
loggers?: {
[name: string]: LoggerConfig;
};
}
2022-11-25 11:40:23 +00:00
/**
* An object that encapsulates provided message and arguments as well some
* metadata that can be later used when formatting a message.
*/
export class LogRecord {
readonly msg: string;
#args: unknown[];
#datetime: Date;
readonly level: number;
readonly levelName: string;
readonly loggerName: string;
constructor(options: LogRecordOptions) {
this.msg = options.msg;
this.#args = [...options.args];
this.level = options.level;
this.loggerName = options.loggerName;
this.#datetime = new Date();
this.levelName = getLevelName(options.level);
}
get args(): unknown[] {
return [...this.#args];
}
get datetime(): Date {
return new Date(this.#datetime.getTime());
}
2019-01-06 19:19:15 +00:00
}
2018-12-19 18:16:45 +00:00
export interface LoggerOptions {
handlers?: BaseHandler[];
}
2018-12-19 18:16:45 +00:00
export class Logger {
#level: LogLevel;
handlers: BaseHandler[];
readonly #loggerName: string;
2018-12-19 18:16:45 +00:00
constructor(
loggerName: string,
levelName: LevelName,
options: LoggerOptions = {},
) {
this.#loggerName = loggerName;
this.#level = getLevelByName(levelName);
this.handlers = options.handlers || [];
}
/** Use this to retrieve the current numeric log level. */
get level(): LogLevel {
return this.#level;
}
/** Use this to set the numeric log level. */
set level(level: LogLevel) {
try {
this.#level = getLevelByName(getLevelName(level));
} catch (_) {
throw new TypeError(`Invalid log level: ${level}`);
}
}
2019-01-06 19:19:15 +00:00
get levelName(): LevelName {
return getLevelName(this.#level);
}
set levelName(levelName: LevelName) {
this.#level = getLevelByName(levelName);
}
get loggerName(): string {
return this.#loggerName;
}
/**
* If the level of the logger is greater than the level to log, then nothing
* is logged, otherwise a log record is passed to each log handler. `msg` data
* passed in is returned. If a function is passed in, it is only evaluated
* if the msg will be logged and the return value will be the result of the
* function, not the function itself, unless the function isn't called, in which
* case undefined is returned. All types are coerced to strings for logging.
*/
#log<T>(
level: LogLevel,
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
if (this.level > level) {
return msg instanceof Function ? undefined : msg;
}
2019-01-02 14:12:48 +00:00
let fnResult: T | undefined;
let logMessage: string;
if (msg instanceof Function) {
fnResult = msg();
logMessage = this.asString(fnResult);
} else {
logMessage = this.asString(msg);
}
const record: LogRecord = new LogRecord({
msg: logMessage,
args: args,
level: level,
loggerName: this.loggerName,
});
this.handlers.forEach((handler) => {
handler.handle(record);
});
return msg instanceof Function ? fnResult : msg;
}
asString(data: unknown, isProperty = false): string {
if (typeof data === "string") {
if (isProperty) return `"${data}"`;
return data;
} else if (
data === null ||
typeof data === "number" ||
typeof data === "bigint" ||
typeof data === "boolean" ||
typeof data === "undefined" ||
typeof data === "symbol"
) {
return String(data);
} else if (data instanceof Error) {
return data.stack!;
} else if (typeof data === "object") {
return `{${
Object.entries(data)
.map(([k, v]) => `"${k}":${this.asString(v, true)}`)
.join(",")
}}`;
}
return "undefined";
2018-12-19 18:16:45 +00:00
}
debug<T>(msg: () => T, ...args: unknown[]): T | undefined;
debug<T>(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T;
debug<T>(
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
return this.#log(LogLevels.DEBUG, msg, ...args);
2018-12-19 18:16:45 +00:00
}
info<T>(msg: () => T, ...args: unknown[]): T | undefined;
info<T>(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T;
info<T>(
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
return this.#log(LogLevels.INFO, msg, ...args);
2018-12-19 18:16:45 +00:00
}
/** @deprecated (will be removed after 0.214.0) Use {@linkcode warn} instead. */
warning<T>(msg: () => T, ...args: unknown[]): T | undefined;
/** @deprecated (will be removed after 0.214.0) Use {@linkcode warn} instead. */
warning<T>(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T;
warning<T>(
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
return this.warn(msg as () => T, ...args);
}
warn<T>(msg: () => T, ...args: unknown[]): T | undefined;
warn<T>(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T;
warn<T>(
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
return this.#log(LogLevels.WARNING, msg, ...args);
2018-12-19 18:16:45 +00:00
}
error<T>(msg: () => T, ...args: unknown[]): T | undefined;
error<T>(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T;
error<T>(
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
return this.#log(LogLevels.ERROR, msg, ...args);
2018-12-19 18:16:45 +00:00
}
critical<T>(msg: () => T, ...args: unknown[]): T | undefined;
critical<T>(
msg: T extends GenericFunction ? never : T,
...args: unknown[]
): T;
critical<T>(
msg: (T extends GenericFunction ? never : T) | (() => T),
...args: unknown[]
): T | undefined {
return this.#log(LogLevels.CRITICAL, msg, ...args);
2018-12-19 18:16:45 +00:00
}
}