UNPKG

@akala/core

Version:
269 lines (247 loc) 7.76 kB
import { bold, italic, strikethrough, underline } from 'yoctocolors' import { Middleware, MiddlewareAsync, MiddlewareResult } from '../middlewares/shared.js'; import { each } from '../each.js'; /** * Enum representing logging levels mapped to numeric priorities * @enum {number} * @property {number} error - Highest priority error level (0) * @property {number} warn - Warning level (1) * @property {number} help - Help information (2) * @property {number} data - Data tracing (3) * @property {number} info - General information (4) * @property {number} debug - Debug-level messages (5) * @property {number} prompt - Prompt messages (6) * @property {number} verbose - Verbose output (7) * @property {number} input - Input tracing (8) * @property {number} silly - Lowest priority tracing (9) */ export enum LogLevels { error = 0, warn = 1, help = 2, data = 3, info = 4, debug = 5, prompt = 6, verbose = 7, input = 8, silly = 9 } export interface LogContext { level: LogLevels; namespaces: string[]; message: string; values: unknown[]; } export interface ILogMiddleware extends Middleware<[LogLevels, string[], ...unknown[]]> { shouldHandle(logLevel: LogLevels, namespaces: string[]): boolean; } export interface ILogMiddlewareAsync extends MiddlewareAsync<[LogLevels, string[], ...unknown[]]> { shouldHandle(logLevel: LogLevels, namespaces: string[]): boolean; } export type ILogger<TLogger = ILogMiddleware> = { [key in Exclude<keyof typeof LogLevels, number>]: TLogger } export type ILoggerAsync = ILogger<ILogMiddlewareAsync> export class LoggerAdapterMiddleware< TLogger extends ILogger | ILoggerAsync > { constructor(private readonly logger: TLogger) { } handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): TLogger extends ILoggerAsync ? Promise<MiddlewareResult> : MiddlewareResult { return this.logger[LogLevels[logLevel]].handle(logLevel, namespaces, ...context); } } export const logConfig: { defaultLevel: LogLevels; namespaceConfig: LogConfig } = { defaultLevel: LogLevels.error, namespaceConfig: {} }; export function configureLogging(config: { defaultLevel?: LogLevels, namespaceConfig?: EasyLogConfig }) { if (typeof config.defaultLevel !== 'undefined') logConfig.defaultLevel = config.defaultLevel; if (typeof config.namespaceConfig != 'undefined') { deepMerge(logConfig.namespaceConfig, config.namespaceConfig); } } function deepMerge(a: LogConfig, b: EasyLogConfig) { each(b, (c, k) => { if (typeof a[k] == 'undefined') if (typeof c == 'number') a[k] = { level: c }; else deepMerge(a[k] = {}, c); else if (typeof c == 'number') a[k].level = c; else deepMerge(a[k] as unknown as LogConfig, c); }) } type LogConfig = { [key: string]: { level?: LogLevels } & LogConfig }; export type EasyLogConfig = { [key: string]: LogLevels | EasyLogConfig }; export class LoggerMiddleware< TLogger extends ILogMiddleware | ILogMiddlewareAsync > { constructor(private readonly logger: TLogger['handle'] | ((...args: Parameters<TLogger['handle']>) => void), public readonly logLevel: LogLevels, public readonly namespace: string) { } handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): Promise<MiddlewareResult> handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): MiddlewareResult handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): TLogger extends ILogMiddlewareAsync ? Promise<MiddlewareResult> : MiddlewareResult handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): Promise<MiddlewareResult> | MiddlewareResult handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): Promise<MiddlewareResult> | MiddlewareResult { if (this.shouldHandle(logLevel, namespaces)) return this.logger(logLevel, namespaces, ...context) as TLogger extends ILogMiddlewareAsync ? Promise<MiddlewareResult> : MiddlewareResult; } shouldHandle( logLevel: LogLevels, namespaces: string[] ): boolean { if (this.logLevel < logLevel) return false; if (this.namespace !== '*' && this.namespace !== namespaces[0]) return false return true; } } export class LogMiddlewareWrapper< TLogger extends ILogMiddleware | ILogMiddlewareAsync > { constructor(private readonly logger: TLogger, public readonly logLevel: LogLevels, public readonly namespace: string) { } handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): Promise<MiddlewareResult> handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): MiddlewareResult handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): TLogger extends ILogMiddlewareAsync ? Promise<MiddlewareResult> : MiddlewareResult handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): Promise<MiddlewareResult> | MiddlewareResult handle( logLevel: LogLevels, namespaces: string[], ...context: unknown[] ): Promise<MiddlewareResult> | MiddlewareResult { if (this.shouldHandle(logLevel, namespaces)) return this.logger.handle(logLevel, namespaces, ...context) as TLogger extends ILogMiddlewareAsync ? Promise<MiddlewareResult> : MiddlewareResult; return; } shouldHandle( logLevel: LogLevels, namespaces: string[] ): boolean { if (this.logLevel < logLevel) return false; if (this.namespace !== '*' && this.namespace != namespaces[0]) return false return this.logger.shouldHandle(logLevel, namespaces.slice(1)); } } export const emojiMap = { smile: '😄', thumbsup: '👍', heart: '❤️', fire: '🔥', star: '⭐', cry: '😢', laugh: '😂', wink: '😉', clap: '👏', angry: '😠', shocked: '😲', cool: '😎', poop: '💩', party: '🥳', thinking: '🤔', pray: '🙏', hug: '🤗', ok: '👌', eyes: '👀', grin: '😁', sleepy: '😴', kiss: '😘', celebration: '🎉', check: '✅', cross: '❌', question: '❓', wave: '👋', rocket: '🚀', 100: '💯' }; const oldErrorLog = console.error; console.error = function (format, ...args) { if (typeof format == 'string') oldErrorLog.call(console, format .replace(/__((?:[^_]|_[^_])+)__/g, (_, text) => bold(text)) .replace(/\*\*((?:[^\*]|\*[^\*])+)\*\*/g, (_, text) => bold(text)) .replace(/_([^_]+)_/g, (_, text) => underline(text)) .replace(/\*([^\*]+)\*/g, (_, text) => italic(text)) .replace(/```.*\n((?:[^`]|\n)+)\n```/g, (_, text) => italic(bold(text))) .replace(/`([^`]+)`/g, (_, text) => italic(bold(text))) .replace(/~~((?:[^~]|~[^~])+)~~/g, (_, text) => strikethrough(text)) .replace(/:([a-z_]+):/g, (_, emojiName) => emojiMap[emojiName] || `:${emojiName}:`) , ...args); else oldErrorLog.call(console, format, ...args); }