import { objectRecord } from "../../../objects/funcs/objectRecord" import { fmt } from "../../funcs/fmt" import { attachConsole, unattachConsole } from "./feat/attachConsole" import { toJson } from "./feat/toJson" import { ILogRecord, LogLv } from "./types" export interface ILoggerOptions { /** 启用日志记录 */ enableRec?: boolean /** 启用日志格式化打印(当打开日志时会格式化输出,不影响记录格式) */ enableFmt?: boolean /** 日志记录的最长数量(内存中的限制,非文件的限制) */ recMaxLength?: number /** 日志记录保存到文件,需要调用者实现,提供一个接口 */ recToFile?: (log: ILogRecord, options: ILoggerOptions) => any /** 单个日志对象保存深度 */ logObjectMaxDepth?: number /** 单个日志对象保存最大字节数 */ logObjectMaxBytes?: number | string /** 开发追踪模式(在控制台使用 warn 都可以全部日志,以检查调用堆栈) */ isDevTrace?: boolean /** 关闭日志显示输出 */ disableDisplay?: boolean /** 异步显示日志输出 */ asyncDisplay?: boolean /** 要记录的日志级别,记录大小于等于此等级的日志,默认为 LogLv.Log (1) */ recLv?: LogLv } /** * 日志记录工具 * - 可以记录日志到内存或文件 * - 可以自动进行格式化日志输出 * - 给日志附带上下文信息 * - 支持异步显示日志输出 * - 支持劫持控制台日志输出(隐藏日志) */ export class Logger> { options!: ILoggerOptions ctx?: TCtx rootLogger?: Logger recLogs: ILogRecord[] = [] constructor(ctx?: TCtx, options?: Partial, rootLogger?: Logger) { this.ctx = ctx this.options = Object.assign( { enableRec: true, recMaxLength: 3000, logObjectMaxDepth: 256, logObjectMaxBytes: "1MB", recFileMaxBytes: "4GB", recLv: LogLv.Log, }, this.options, options ) this.rootLogger = rootLogger } attachConsole = attachConsole unattachConsole = unattachConsole toJson = toJson // ------------------ // ------------------ log(...args: any[]) { this.#log("log", LogLv.Log, args) } info(...args: any[]) { this.#log("info", LogLv.Info, args) } warn(...args: any[]) { this.#log("warn", LogLv.Warn, args) } error(...args: any[]) { this.#log("error", LogLv.Error, args) } debug(...args: any[]) { this.#log("debug", LogLv.Debug, args) } group(...args: any[]) { this.#log("group", LogLv.Log, args) } groupEnd(...args: any[]) { this.#log("groupEnd", LogLv.Log, args) } // ------------------ __raw_console__: Record void> = {} // ------------------ #log(type: string, lv: LogLv, args: any[]) { this.#logPrint(type, args) if (this.options.enableRec && lv >= (this.options.recLv || LogLv.Log)) { this.#logRecord(type, lv, args) } } /** 打印日志到控制台 */ #logPrint(type: string, args: any[]) { if (this.options.disableDisplay) return if (this.options.isDevTrace) type = "warn" if (this.options.enableFmt) args = fmt(...args) const log: any = this.__raw_console__[type] || (console as any)[type] if (this.options.asyncDisplay) { queueMicrotask(() => log.apply(console, args)) } else { log.apply(console, args) } } #logRecord(type: string, lv: LogLv, args: any[]) { queueMicrotask(() => { let logRec = objectRecord( { args, ctx: this.ctx as any, lv, type, time: Date.now(), }, { maxDepth: this.options.logObjectMaxDepth, maxBytes: this.options.logObjectMaxBytes, } ) as ILogRecord if (this.options.recToFile) { this.options.recToFile(logRec, this.options) } else { let rootLogger = this.rootLogger || this rootLogger.recLogs.push(logRec) if (rootLogger.recLogs.length > (this.options.recMaxLength || 0)) { rootLogger.recLogs.shift() } } }) } }