UNPKG

@roboplay/sage

Version:
278 lines (276 loc) 7.26 kB
import { inspect } from 'node:util'; import { color } from './color.js'; const DEBUG_MODE = process.env.NODE_ENV !== "production"; const ANSI_REGEX = /\x1b\[.*?m/g; const pendingDrains = /* @__PURE__ */ new Set(); function consoleDrain(logger2, level, ...data) { switch (level) { case "trace": case "debug": case "info": case "wait": case "event": return writeLog(process.stdout, ...data); case "warn": case "error": return writeLog(process.stderr, ...data); default: return writeLog(process.stdout, ...data); } } function writeLog(stream, ...data) { return new Promise((resolve, reject) => { const parts = data?.map((item) => { if (typeof item === "object" || item instanceof Error || Array.isArray(item)) { return inspect(item, { colors: true, depth: null }); } return item; }); stream.write(parts?.join(" ") + "\n", "utf8", (error) => { if (error) reject(error); else resolve(); }); }); } const DEFAULT_MAX_ENTRIES = 100; class LogEntry { level; timestamp; data; constructor(level, data) { this.level = level; this.data = data; this.timestamp = /* @__PURE__ */ new Date(); } message() { const messageParts = this.data.map((item) => { if (item instanceof Error) { return item.message; } else if (typeof item === "object") { try { return JSON.stringify(item); } catch (error) { return "[unserializable object]"; } } return item; }); return messageParts.join(" ").replace(ANSI_REGEX, ""); } } class Logger { _customLevels; _enabled; _level; _levelValues; _parent; _prefix; _currentIndex; _drain; _logBuffer; constructor(options) { const { customLevels, drain = consoleDrain, enabled = true, level = "info", parent, prefix } = options ?? {}; this._customLevels = customLevels; this._drain = drain; this._enabled = enabled; this._level = level; this._parent = parent; this._prefix = prefix; this._levelValues = { ...LogLevelValues, ...Object.fromEntries(Object.entries(this._customLevels ?? {}).map(([key, value]) => [key, value.priority])) }; this._currentIndex = 0; this._logBuffer = new Array(options?.maxEntries ?? DEFAULT_MAX_ENTRIES); } _log(level, ...data) { if (this._parent) { data.unshift(this._prefix); return this._parent._log(level, ...data); } if (!this._enabled) { return; } if (this._drain === consoleDrain && this._levelValues[this._level] > this._levelValues[level]) { return; } if (level !== "other") { const label = this._customLevels ? this._customLevels[level]?.label : colorizedLogLevels[level]; data.unshift((label ?? level.padEnd(5)) + " -"); } if (DEBUG_MODE) { this._logBuffer[this._currentIndex] = new LogEntry(level, data); this._currentIndex = (this._currentIndex + 1) % this._logBuffer.length; } const promise = this._drain(this, level, ...data); pendingDrains.add(promise); promise.finally(() => { pendingDrains.delete(promise); }); } /** * Waits for all pending log writes to complete. */ async flush() { if (this._parent) { return this._parent.flush(); } await Promise.allSettled([...pendingDrains]); } /** * Creates a new logger instance with the specified prefix. * This is useful for creating a logger for a specific plugin, big features, or modules. * * All writes and cached logs will be delegated to the parent logger, so debugging will still work. * * @param prefix The prefix to add to the logger (e.g. 'my-plugin') * @returns A new logger instance with the specified prefix */ fork(prefix) { return new Logger({ customLevels: this._customLevels, enabled: this._enabled, level: this._level, parent: this, prefix: this._prefix ? this._prefix + prefix : prefix }); } getLevel() { if (this._parent) { return this._parent.getLevel(); } return this._level; } getLevelValues() { if (this._parent) { return this._parent.getLevelValues(); } return this._levelValues; } getRecentLogs(count = 50) { if (this._parent) { return this._parent.getRecentLogs(count); } if (count <= 0) { return []; } count = Math.min(count, this._logBuffer.length); const startIndex = (this._currentIndex - count + this._logBuffer.length) % this._logBuffer.length; let recentLogs; if (startIndex < this._currentIndex) { recentLogs = this._logBuffer.slice(startIndex, this._currentIndex); } else { recentLogs = this._logBuffer.slice(startIndex).concat(this._logBuffer.slice(0, this._currentIndex)); } return recentLogs.reverse(); } setDrain(drain) { this._drain = drain; } trace(...data) { this._log("trace", ...data); } debug(...data) { this._log("debug", ...data); } info(...data) { this._log("info", ...data); } wait(...data) { this._log("wait", ...data); } log(...data) { this._log("other", ...data); } event(...data) { this._log("event", ...data); } ready(...data) { this._log("ready", ...data); } warn(...data) { this._log("warn", ...data); } error(...data) { this._log("error", ...data); } custom(level, ...data) { if (this._customLevels?.[level]) { this._log(level, ...data); } } } const LogLevelValues = { trace: 0, debug: 1, info: 2, wait: 3, other: 4, event: 5, ready: 6, warn: 7, error: 8 }; const colorizedLogLevels = { trace: color.gray("trace".padEnd(5)), debug: color.cyan("debug".padEnd(5)), info: color.blue("info".padEnd(5)), wait: color.cyan("wait".padEnd(5)), event: color.magenta("event".padEnd(5)), ready: color.green("ready".padEnd(5)), warn: color.yellow("warn".padEnd(5)), error: color.red("error".padEnd(5)) }; let _logger = null; function logger(options) { if (options) { _logger = new Logger(options); } else if (!_logger) { _logger = new Logger(); } return _logger; } logger.flush = async function() { await logger().flush(); }; logger.fork = function(prefix) { return logger().fork(prefix); }; logger.getRecentLogs = function(count = 25) { return logger().getRecentLogs(count); }; logger.trace = function(...data) { return logger().trace(...data); }; logger.debug = function(...data) { return logger().debug(...data); }; logger.info = function(...data) { return logger().info(...data); }; logger.wait = function(...data) { return logger().wait(...data); }; logger.log = function(...data) { return logger().log(...data); }; logger.event = function(...data) { return logger().event(...data); }; logger.ready = function(...data) { return logger().ready(...data); }; logger.warn = function(...data) { return logger().warn(...data); }; logger.error = function(...data) { return logger().error(...data); }; logger.custom = function(level, ...data) { return logger().custom(level, ...data); }; export { ANSI_REGEX, DEBUG_MODE, Logger, logger }; //# sourceMappingURL=out.js.map //# sourceMappingURL=logger.js.map