UNPKG

@anishsharma/betterlogs

Version:

A lightweight yet powerful logging library that makes console output elegant, expressive, and customizable

451 lines (444 loc) 12.5 kB
// src/utils.ts var EnvironmentDetector = class { static isNode() { return typeof process !== "undefined" && process.versions != null && process.versions.node != null; } static isBrowser() { return typeof window !== "undefined" && typeof document !== "undefined"; } static supportsEmoji() { if (this.isNode()) { const isTTY = process.stdout?.isTTY; const hasNoEmojiFlag = process.env.NO_EMOJI === "true"; return !!isTTY && !hasNoEmojiFlag; } if (this.isBrowser()) { return true; } return true; } }; var Colorizer = class { static applyColor(text, color, isBackground = false) { if (EnvironmentDetector.isNode()) { return this.applyNodeColor(text, color, isBackground); } else { return this.applyBrowserColor(text, color, isBackground); } } static applyNodeColor(text, color, isBackground) { const colors = { red: "\x1B[31m", green: "\x1B[32m", yellow: "\x1B[33m", blue: "\x1B[34m", magenta: "\x1B[35m", cyan: "\x1B[36m", white: "\x1B[37m", gray: "\x1B[90m", reset: "\x1B[0m" }; const bgColors = { red: "\x1B[41m", green: "\x1B[42m", yellow: "\x1B[43m", blue: "\x1B[44m", magenta: "\x1B[45m", cyan: "\x1B[46m", white: "\x1B[47m", reset: "\x1B[0m" }; const code = isBackground ? bgColors[color] : colors[color]; return code ? `${code}${text}${colors.reset}` : text; } static applyBrowserColor(text) { return text; } }; function formatTime(date, format) { const hours = format === "12h" ? date.getHours() % 12 || 12 : date.getHours().toString().padStart(2, "0"); const minutes = date.getMinutes().toString().padStart(2, "0"); const seconds = date.getSeconds().toString().padStart(2, "0"); if (format === "12h") { const ampm = date.getHours() >= 12 ? "PM" : "AM"; return `${hours}:${minutes}:${seconds} ${ampm}`; } return `${hours}:${minutes}:${seconds}`; } // src/formatter.ts var Formatter = class { static formatPretty(entry, config, theme) { const parts = []; if (config.showTimestamp) { const timestamp = formatTime(entry.timestamp, config.timestampFormat); parts.push(`[${timestamp}]`); } if (config.showEmoji) { const levelConfig2 = theme.levels[entry.level] || theme.levels.info; parts.push(levelConfig2.emoji); } const levelConfig = theme.levels[entry.level] || theme.levels.info; const levelStr = entry.level.toUpperCase(); const coloredLevel = Colorizer.applyColor(levelStr, levelConfig.color); parts.push(coloredLevel); if (entry.label) { const labelStr = `[${entry.label}]`; const coloredLabel = Colorizer.applyColor(labelStr, "gray"); parts.push(coloredLabel); } parts.push(entry.message); return parts.join(" "); } static formatJson(entry) { const jsonEntry = { level: entry.level, message: entry.message, timestamp: entry.timestamp.toISOString(), label: entry.label, ...entry.data && entry.data.length > 0 ? { data: entry.data } : {} }; return JSON.stringify(jsonEntry); } static format(entry, config, theme) { if (config.mode === "json") { return this.formatJson(entry); } return this.formatPretty(entry, config, theme); } }; // src/fileLogger.ts import { writeFileSync, appendFileSync, existsSync, mkdirSync } from "fs"; import { dirname } from "path"; var FileLogger = class { constructor(filePath) { this.filePath = filePath; this.ensureDirectoryExists(); this.initializeFile(); } write(entry) { const logLine = this.formatLogEntry(entry); try { appendFileSync(this.filePath, logLine + "\n", "utf8"); } catch (error) { console.error("Failed to write to log file:", error); } } ensureDirectoryExists() { const dir = dirname(this.filePath); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } } initializeFile() { if (!existsSync(this.filePath)) { writeFileSync(this.filePath, "", "utf8"); } } formatLogEntry(entry) { return JSON.stringify({ timestamp: entry.timestamp.toISOString(), level: entry.level, message: entry.message, label: entry.label, ...entry.data && { data: entry.data } }); } }; // src/logger.ts var BetterLogger = class _BetterLogger { constructor(configManager2, themeManager2, label) { this.labels = /* @__PURE__ */ new Map(); this.customLevels = /* @__PURE__ */ new Map(); this.activeTimers = /* @__PURE__ */ new Map(); this.configManager = configManager2; this.themeManager = themeManager2; this.label = label; if (EnvironmentDetector.isNode() && this.configManager.getConfig().file) { this.fileLogger = new FileLogger( this.configManager.getConfig().file ); } } // Core logging methods info(message, ...data) { this.log("info", message, data); } success(message, ...data) { this.log("success", message, data); } warn(message, ...data) { this.log("warn", message, data); } error(message, ...data) { this.log("error", message, data); } debug(message, ...data) { this.log("debug", message, data); } // Label system withLabel(labelName) { if (!this.labels.has(labelName)) { this.labels.set( labelName, new _BetterLogger( this.configManager, this.themeManager, labelName ) ); } return this.labels.get(labelName); } // Configuration config(newConfig) { this.configManager.updateConfig(newConfig); } setLevel(level) { this.configManager.updateConfig({ level }); } setMode(mode) { this.configManager.updateConfig({ mode }); } // Custom levels addLevel(name, config) { this.customLevels.set(name, config); const currentTheme = this.configManager.getCurrentTheme(); if (!currentTheme.levels[name]) { currentTheme.levels[name] = config; } this[name] = (message, ...data) => { this.log(name, message, data); }; } // Grouped logs group(name) { return this.withLabel(name); } // Table logging table(data) { if (console.table) { console.table(data); } else { console.log(JSON.stringify(data, null, 2)); } } // Timer utilities time(label) { this.activeTimers.set(label, Date.now()); } timeEnd(label) { const startTime = this.activeTimers.get(label); if (startTime) { const duration = Date.now() - startTime; this.info(`Timer '${label}': ${duration}ms`); this.activeTimers.delete(label); } } // File logging (Node.js only) file(filePath) { if (EnvironmentDetector.isNode()) { this.configManager.updateConfig({ file: filePath }); this.fileLogger = new FileLogger(filePath); } else { this.warn("File logging is only available in Node.js environment"); } } // Private methods log(level, message, data) { const levelToCheck = this.customLevels.has(level) ? "debug" : level; if (!this.configManager.shouldLog(levelToCheck)) { return; } const entry = { level, message, timestamp: /* @__PURE__ */ new Date(), label: this.label, data: data.length > 0 ? data : void 0 }; const config = this.configManager.getConfig(); const theme = this.configManager.getCurrentTheme(); if (!theme.levels[level] && this.customLevels.has(level)) { theme.levels[level] = this.customLevels.get(level); } const formattedMessage = Formatter.format(entry, config, theme); this.outputToConsole(level, formattedMessage, data); if (this.fileLogger) { this.fileLogger.write(entry); } } outputToConsole(level, formattedMessage, data) { const consoleMethod = this.getConsoleMethod(level); if (data.length > 0) { consoleMethod(formattedMessage, ...data); } else { consoleMethod(formattedMessage); } } getConsoleMethod(level) { switch (level) { case "error": return console.error; case "warn": return console.warn; case "debug": return console.debug; default: return console.log; } } }; // src/config.ts var defaultConfig = { showTimestamp: true, showEmoji: true, theme: "dark", level: "info", mode: "pretty", timestampFormat: "24h" }; var ConfigManager = class { constructor(themeManager2) { this.themeManager = themeManager2; this.config = { ...defaultConfig }; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; } getConfig() { return { ...this.config }; } getCurrentTheme() { const theme = this.config.theme; if (typeof theme === "string") { return this.themeManager.getTheme(theme) || this.themeManager.getDefaultTheme(); } return theme; } shouldLog(level) { const levelWeights = { debug: 0, info: 1, success: 1, warn: 2, error: 3, silent: 999 }; return levelWeights[level] >= levelWeights[this.config.level]; } }; // src/themes.ts var builtInThemes = { dark: { name: "dark", levels: { info: { color: "cyan", emoji: "\u{1F4A1}" }, success: { color: "green", emoji: "\u2705" }, warn: { color: "yellow", emoji: "\u26A0\uFE0F" }, error: { color: "red", emoji: "\u274C" }, debug: { color: "magenta", emoji: "\u{1F50D}" } }, background: "#1a1a1a" }, light: { name: "light", levels: { info: { color: "blue", emoji: "\u{1F4A1}" }, success: { color: "green", emoji: "\u2705" }, warn: { color: "orange", emoji: "\u26A0\uFE0F" }, error: { color: "red", emoji: "\u274C" }, debug: { color: "purple", emoji: "\u{1F50D}" } }, background: "#ffffff" }, neon: { name: "neon", levels: { info: { color: "#00FFFF", emoji: "\u{1F4A1}" }, success: { color: "#00FF7F", emoji: "\u2705" }, warn: { color: "#FFD700", emoji: "\u26A0\uFE0F" }, error: { color: "#FF5555", emoji: "\u274C" }, debug: { color: "#8888FF", emoji: "\u{1F50D}" } }, background: "#111111" }, minimal: { name: "minimal", levels: { info: { color: "gray", emoji: "\u2022" }, success: { color: "gray", emoji: "\u2713" }, warn: { color: "gray", emoji: "!" }, error: { color: "gray", emoji: "\u2717" }, debug: { color: "gray", emoji: ">" } } } }; var ThemeManager = class { constructor() { this.themes = /* @__PURE__ */ new Map(); Object.values(builtInThemes).forEach((theme) => { this.registerTheme(theme); }); } registerTheme(theme) { this.themes.set(theme.name, theme); } getTheme(name) { return this.themes.get(name); } getDefaultTheme() { return builtInThemes.dark; } }; // src/index.ts var themeManager = new ThemeManager(); var configManager = new ConfigManager(themeManager); var baseLogger = new BetterLogger(configManager, themeManager); var betterlogs = { // Core logging methods info: baseLogger.info.bind(baseLogger), success: baseLogger.success.bind(baseLogger), warn: baseLogger.warn.bind(baseLogger), error: baseLogger.error.bind(baseLogger), debug: baseLogger.debug.bind(baseLogger), // Label system label: baseLogger.withLabel.bind(baseLogger), // Configuration config: baseLogger.config.bind(baseLogger), setLevel: (level) => baseLogger.setLevel(level), setMode: baseLogger.setMode.bind(baseLogger), // Advanced features addLevel: (name, config) => { baseLogger.addLevel(name, config); betterlogs[name] = (message, ...data) => { const loggerWithCustomMethod = baseLogger; loggerWithCustomMethod[name](message, ...data); }; }, group: baseLogger.group.bind(baseLogger), table: baseLogger.table.bind(baseLogger), time: baseLogger.time.bind(baseLogger), timeEnd: baseLogger.timeEnd.bind(baseLogger), file: baseLogger.file.bind(baseLogger), // Theme management addTheme: (theme) => themeManager.registerTheme(theme), // Create new instance create: (config) => { const newConfigManager = new ConfigManager(themeManager); if (config) { newConfigManager.updateConfig(config); } return new BetterLogger(newConfigManager, themeManager); } }; var index_default = betterlogs; export { BetterLogger, ConfigManager, ThemeManager, betterlogs, index_default as default };