UNPKG

@loglayer/transport-simple-pretty-terminal

Version:

Pretty log output in the terminal / browser / Next.js for the LogLayer logging library.

676 lines (645 loc) 24 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/SimplePrettyTerminalTransport.ts var _transport = require('@loglayer/transport'); var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk); _createStarExport(_chalk); // src/themes.ts var moonlight = { colors: { trace: _chalk2.default.rgb(114, 135, 153), // Muted blue-grey for less important info debug: _chalk2.default.rgb(130, 170, 255), // Soft blue that pops but doesn't strain info: _chalk2.default.rgb(195, 232, 141), // Sage green for good readability warn: _chalk2.default.rgb(255, 203, 107), // Warm yellow, less harsh than pure yellow error: _chalk2.default.rgb(247, 118, 142), // Soft red that stands out without being aggressive fatal: _chalk2.default.bgRgb(247, 118, 142).white // Inverted soft red for maximum visibility }, logIdColor: _chalk2.default.rgb(84, 98, 117), // Darker blue-grey for secondary information dataValueColor: _chalk2.default.rgb(209, 219, 231), // Light grey-blue for primary content dataKeyColor: _chalk2.default.rgb(130, 170, 255) // Matching debug blue for consistency }; var sunlight = { colors: { trace: _chalk2.default.rgb(110, 110, 110), // Dark grey for subtle information debug: _chalk2.default.rgb(32, 96, 159), // Deep blue for strong contrast on white info: _chalk2.default.rgb(35, 134, 54), // Forest green, easier on the eyes than bright green warn: _chalk2.default.rgb(176, 95, 0), // Brown-orange for natural warning color error: _chalk2.default.rgb(191, 0, 0), // Deep red for clear visibility on light backgrounds fatal: _chalk2.default.bgRgb(191, 0, 0).white // White on deep red for critical issues }, logIdColor: _chalk2.default.rgb(110, 110, 110), dataValueColor: _chalk2.default.rgb(0, 0, 0), dataKeyColor: _chalk2.default.rgb(32, 96, 159) }; var neon = { colors: { trace: _chalk2.default.rgb(108, 108, 255), // Electric blue debug: _chalk2.default.rgb(255, 82, 246), // Hot pink info: _chalk2.default.rgb(0, 255, 163), // Cyber green warn: _chalk2.default.rgb(255, 231, 46), // Electric yellow error: _chalk2.default.rgb(255, 53, 91), // Neon red fatal: _chalk2.default.bgRgb(255, 53, 91).rgb(0, 255, 163) // Neon red bg with cyber green text }, logIdColor: _chalk2.default.rgb(187, 134, 252), // Bright purple dataValueColor: _chalk2.default.rgb(255, 255, 255), // Pure white dataKeyColor: _chalk2.default.rgb(0, 255, 240) // Cyan }; var nature = { colors: { trace: _chalk2.default.rgb(101, 115, 126), // Slate grey debug: _chalk2.default.rgb(34, 139, 34), // Forest green info: _chalk2.default.rgb(46, 139, 87), // Sea green warn: _chalk2.default.rgb(218, 165, 32), // Golden rod error: _chalk2.default.rgb(139, 69, 19), // Saddle brown fatal: _chalk2.default.bgRgb(139, 69, 19).white // Brown background with white text }, logIdColor: _chalk2.default.rgb(101, 115, 126), dataValueColor: _chalk2.default.rgb(0, 0, 0), dataKeyColor: _chalk2.default.rgb(34, 139, 34) }; var pastel = { colors: { trace: _chalk2.default.rgb(200, 200, 200), // Light grey debug: _chalk2.default.rgb(173, 216, 230), // Light blue info: _chalk2.default.rgb(144, 238, 144), // Light green warn: _chalk2.default.rgb(255, 218, 185), // Peach error: _chalk2.default.rgb(255, 182, 193), // Light pink fatal: _chalk2.default.bgRgb(255, 182, 193).rgb(105, 105, 105) // Light pink bg with dim grey text }, logIdColor: _chalk2.default.rgb(200, 200, 200), dataValueColor: _chalk2.default.rgb(105, 105, 105), dataKeyColor: _chalk2.default.rgb(173, 216, 230) }; // src/vendor/prettyjson.js // src/vendor/utils.js function indent(numSpaces) { return new Array(numSpaces + 1).join(" "); } function getMaxIndexLength(input) { var maxWidth = 0; Object.getOwnPropertyNames(input).forEach((key) => { if (input[key] === void 0) { return; } maxWidth = Math.max(maxWidth, key.length); }); return maxWidth; } function isIsoStringDate(isoString) { if (typeof isoString !== "string") { return false; } if (!/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$/.test(isoString)) { return false; } const testDate = new Date(isoString); return !Number.isNaN(testDate.getTime()); } // src/vendor/prettyjson.js var conflictChars = /[^\w\s\n\r\v\t.,]/i; var isPrintable = (input, options) => input !== void 0 || options.renderUndefined; var isSerializable = (input, onlyPrimitives, options) => { if (typeof input === "boolean" || typeof input === "number" || typeof input === "function" || input === null || input === void 0 || input instanceof Date) { return true; } if (typeof input === "string" && input.indexOf("\n") === -1) { return true; } if (options.inlineArrays && !onlyPrimitives) { if (Array.isArray(input) && isSerializable(input[0], true, options)) { return true; } } return false; }; var getColorRenderer = (colorFn, options) => { if (options.noColor) { return (text) => text; } if (typeof colorFn !== "function") { return (text) => text; } return colorFn; }; var addColorToData = (input, options) => { if (options.noColor) { return input; } if (input instanceof Date) { return getColorRenderer(options.dateColor, options)(input.toISOString()); } if (typeof input === "string") { if (isIsoStringDate(input)) { return getColorRenderer(options.dateColor, options)(input); } return getColorRenderer(options.stringColor, options)(input); } const sInput = `${input}`; if (typeof input === "boolean") { return getColorRenderer(options.booleanColor, options)(sInput); } if (input === null || input === void 0) { return getColorRenderer(options.nullUndefinedColor, options)(sInput); } if (typeof input === "number") { if (input >= 0) { return getColorRenderer(options.positiveNumberColor, options)(sInput); } return getColorRenderer(options.negativeNumberColor, options)(sInput); } if (typeof input === "function") { return "function() {}"; } if (Array.isArray(input)) { return input.join(", "); } return sInput; }; var colorMultilineString = (options, line) => getColorRenderer(options.multilineStringColor, options)(line); var indentLines = (string, spaces, options) => { let lines = string.split("\n"); lines = lines.map((line) => indent(spaces) + colorMultilineString(options, line)); return lines.join("\n"); }; var renderToArray = (data, options, indentation) => { if (typeof data === "string" && data.match(conflictChars) && options.escape) { data = JSON.stringify(data); } if (!isPrintable(data, options)) { return []; } if (isSerializable(data, false, options)) { return [indent(indentation) + addColorToData(data, options)]; } if (typeof data === "string") { return [ indent(indentation) + colorMultilineString(options, '"""'), indentLines(data, indentation + options.defaultIndentation, options), indent(indentation) + colorMultilineString(options, '"""') ]; } if (Array.isArray(data)) { if (data.length === 0) { return [indent(indentation) + options.emptyArrayMsg]; } if (options.collapseArrays && data.length > 0) { return [`${indent(indentation)}[... ${data.length} items]`]; } const outputArray = []; data.forEach((element) => { if (!isPrintable(element, options)) { return; } let line = "- "; line = getColorRenderer(options.dashColor, options)(line); line = indent(indentation) + line; if (isSerializable(element, false, options)) { line += renderToArray(element, options, 0)[0]; outputArray.push(line); } else { outputArray.push(line); outputArray.push.apply(outputArray, renderToArray(element, options, indentation + options.defaultIndentation)); } }); return outputArray; } if (data instanceof Error) { return renderToArray( { message: data.message, stack: data.stack.split("\n") }, options, indentation ); } const maxIndexLength = options.noAlign ? 0 : getMaxIndexLength(data); let key; const output = []; Object.getOwnPropertyNames(data).forEach((i) => { if (!isPrintable(data[i], options)) { return; } key = `${i}: `; key = getColorRenderer(options.keysColor, options)(key); key = indent(indentation) + key; if (isSerializable(data[i], false, options)) { const nextIndentation = options.noAlign ? 0 : maxIndexLength - i.length; key += renderToArray(data[i], options, nextIndentation)[0]; output.push(key); } else { output.push(key); output.push.apply(output, renderToArray(data[i], options, indentation + options.defaultIndentation)); } }); return output; }; var validateOptionsAndSetDefaults = (options) => { options = options || {}; options.emptyArrayMsg = options.emptyArrayMsg || "(empty array)"; options.keysColor = options.keysColor || _chalk2.default.green; options.dashColor = options.dashColor || _chalk2.default.green; options.booleanColor = options.booleanColor || _chalk2.default.cyan; options.nullUndefinedColor = options.nullUndefinedColor || _chalk2.default.grey; options.numberColor = options.numberColor || _chalk2.default.blue; options.positiveNumberColor = options.positiveNumberColor || options.numberColor; options.negativeNumberColor = options.negativeNumberColor || options.numberColor; options.dateColor = options.dateColor || _chalk2.default.magenta; options.defaultIndentation = options.defaultIndentation || 2; options.noColor = !!options.noColor; options.noAlign = !!options.noAlign; options.escape = !!options.escape; options.renderUndefined = !!options.renderUndefined; options.collapseArrays = !!options.collapseArrays; options.stringColor = options.stringColor || null; options.multilineStringColor = options.multilineStringColor || options.stringColor || null; return options; }; function render(data, options, indentation) { indentation = indentation || 0; options = validateOptionsAndSetDefaults(options); return renderToArray(data, options, indentation).join("\n"); } // src/views/utils.ts var _datefns = require('date-fns'); function formatTimestamp(timestamp, colorFn, timestampFormat = "HH:mm:ss.SSS") { let formattedTime; if (typeof timestampFormat === "function") { formattedTime = timestampFormat(timestamp); } else { const date = new Date(timestamp); formattedTime = _datefns.format.call(void 0, date, timestampFormat); } return colorFn(`[${formattedTime}]`); } function getLevelColor(level, colors) { const levelLower = level.toLowerCase(); switch (levelLower) { case "trace": return colors.trace || _chalk2.default.white; case "debug": return colors.debug || _chalk2.default.white; case "info": return colors.info || _chalk2.default.white; case "warn": return colors.warn || _chalk2.default.white; case "error": return colors.error || _chalk2.default.white; case "fatal": return colors.fatal || _chalk2.default.white; default: return colors.info || _chalk2.default.white; } } function formatValue(value, expanded = false, collapseArrays = true) { if (typeof value === "string") return value; if (typeof value === "number" || typeof value === "boolean") return value.toString(); if (value === null) return "null"; if (value === void 0) return "undefined"; if (Array.isArray(value)) return expanded ? JSON.stringify(value) : collapseArrays ? "[...]" : value.toString(); if (typeof value === "object") return expanded ? JSON.stringify(value) : "{...}"; return value.toString(); } function formatInlineData(data, config, maxDepth, expanded = false, collapseArrays = true) { if (!data) return ""; const pairs = []; const traverse = (obj, prefix = "", depth = 0) => { if (!expanded && depth >= maxDepth) return; for (const [key, value] of Object.entries(obj)) { const fullKey = prefix ? `${prefix}.${key}` : key; if (value && typeof value === "object" && !Array.isArray(value)) { if (expanded) { pairs.push(`${config.dataKeyColor(fullKey)}=${config.dataValueColor(JSON.stringify(value))}`); } else { traverse(value, fullKey, depth + 1); } } else { pairs.push( `${config.dataKeyColor(fullKey)}=${config.dataValueColor(formatValue(value, expanded, collapseArrays))}` ); } } }; traverse(data); const result = pairs.join(" "); return result; } // src/views/SimpleView.ts var SimpleView = class { constructor(config) { this.config = config; this.viewMode = config.viewMode; this.maxInlineDepth = config.maxInlineDepth; this.showLogId = config.showLogId; this.timestampFormat = config.timestampFormat; this.collapseArrays = config.collapseArrays !== false; this.flattenNestedObjects = config.flattenNestedObjects !== false; this.runtime = config.runtime; this.includeDataInBrowserConsole = config.includeDataInBrowserConsole || false; } getConfig() { return this.config; } getViewMode() { return this.viewMode; } /** * Writes a message to the appropriate output based on runtime */ writeMessage(message, level, data) { if (this.runtime === "node") { _optionalChain([process, 'optionalAccess', _ => _.stdout, 'optionalAccess', _2 => _2.write, 'optionalCall', _3 => _3(`${message} `)]); } else { const shouldIncludeData = this.includeDataInBrowserConsole && data !== void 0; switch (level) { case "trace": if (shouldIncludeData) { console.debug(message, data); } else { console.debug(message); } break; case "debug": if (shouldIncludeData) { console.debug(message, data); } else { console.debug(message); } break; case "info": if (shouldIncludeData) { console.info(message, data); } else { console.info(message); } break; case "warn": if (shouldIncludeData) { console.warn(message, data); } else { console.warn(message); } break; case "error": case "fatal": if (shouldIncludeData) { console.error(message, data); } else { console.error(message); } break; default: if (shouldIncludeData) { console.log(message, data); } else { console.log(message); } break; } } } /** * Renders a single log entry based on the current view mode */ renderLogLine(entry) { const levelColor = getLevelColor(entry.level, this.config.config.colors); const chevron = levelColor(`\u25B6 ${entry.level.toUpperCase()} `); const message = entry.message || "(no message)"; const timestamp = formatTimestamp(entry.timestamp, this.config.config.logIdColor, this.timestampFormat); const parsedData = entry.data ? JSON.parse(entry.data) : void 0; switch (this.viewMode) { case "message-only": { const condensedLine = `${timestamp} ${chevron}${message}`; this.writeMessage(condensedLine, entry.level, parsedData); break; } case "inline": { const logId = this.showLogId ? this.config.config.logIdColor(`[${entry.id}]`) : ""; const fullData = entry.data ? formatInlineData( JSON.parse(entry.data), this.config.config, this.maxInlineDepth, !this.flattenNestedObjects, this.collapseArrays ) : ""; const expandedLine = `${timestamp} ${chevron}${logId ? `${logId} ` : ""}${message}${fullData ? ` ${fullData}` : ""}`; this.writeMessage(expandedLine, entry.level, parsedData); break; } case "expanded": { const logId = this.showLogId ? this.config.config.logIdColor(`[${entry.id}]`) : ""; const firstLine = `${timestamp} ${chevron}${logId ? `${logId} ` : ""}${message}`; this.writeMessage(firstLine, entry.level, parsedData); if (entry.data) { const jsonLines = render(JSON.parse(entry.data), { defaultIndentation: 2, keysColor: this.config.config.dataKeyColor, dashColor: this.config.config.dataKeyColor, numberColor: this.config.config.dataValueColor, stringColor: this.config.config.dataValueColor, multilineStringColor: this.config.config.dataValueColor, positiveNumberColor: this.config.config.dataValueColor, negativeNumberColor: this.config.config.dataValueColor, booleanColor: this.config.config.dataValueColor, nullUndefinedColor: this.config.config.dataValueColor, dateColor: this.config.config.dataValueColor, collapseArrays: this.collapseArrays }).split("\n"); for (const line of jsonLines) { if (line.trim() !== "") { this.writeMessage(` ${line}`, entry.level); } } } break; } default: { const logId = this.showLogId ? this.config.config.logIdColor(`[${entry.id}]`) : ""; const fullData = entry.data ? formatInlineData(JSON.parse(entry.data), this.config.config, this.maxInlineDepth, true, this.collapseArrays) : ""; const expandedLine = `${timestamp} ${chevron}${logId ? `${logId} ` : ""}${message}${fullData ? ` ${fullData}` : ""}`; this.writeMessage(expandedLine, entry.level, parsedData); break; } } } }; // src/SimplePrettyTerminalTransport.ts var SimplePrettyTerminalTransport = class extends _transport.LoggerlessTransport { /** Handles rendering and formatting of logs */ /** Configuration options */ /** * Creates a new SimplePrettyTerminalTransport instance. * * @param config - Configuration options for the transport */ constructor(config) { super(config); this.config = config; if (config.enabled === false) { return; } const maxInlineDepth = config.maxInlineDepth || 4; const theme = config.theme || moonlight; const viewMode = config.viewMode || "inline"; const showLogId = config.showLogId || false; const timestampFormat = config.timestampFormat || "HH:mm:ss.SSS"; const collapseArrays = config.collapseArrays !== false; const flattenNestedObjects = config.flattenNestedObjects !== false; const runtime = config.runtime; const includeDataInBrowserConsole = config.includeDataInBrowserConsole || false; const viewConfig = { colors: { trace: _chalk2.default.gray, // Lowest level, used for verbose output debug: _chalk2.default.blue, // Debug information info: _chalk2.default.green, // Normal operation warn: _chalk2.default.yellow, // Warning conditions error: _chalk2.default.red, // Error conditions fatal: _chalk2.default.bgRed.white, // Critical errors ...theme.colors }, logIdColor: theme.logIdColor || _chalk2.default.dim, dataValueColor: theme.dataValueColor || _chalk2.default.white, dataKeyColor: theme.dataKeyColor || _chalk2.default.dim }; this.renderer = new SimpleView({ viewMode, maxInlineDepth, showLogId, timestampFormat, collapseArrays, flattenNestedObjects, runtime, includeDataInBrowserConsole, config: viewConfig }); } /** * Generates a random ID for each log entry. * Uses base36 encoding for compact, readable IDs. * * @returns A 6-character string ID * @example * "a1b2c3" // Example generated ID */ generateId() { return Math.random().toString(36).substring(2, 8); } /** * Main transport method that receives logs from LogLayer. * This method is called for each log event and handles: * - Generating a unique ID for the log * - Converting the log data to a storable format * - Rendering the log using the SimpleView renderer * * @param logLevel - The severity level of the log * @param messages - Array of message strings to be joined * @param data - Additional structured data to be logged * @param hasData - Whether the log includes additional data * @returns The original messages array */ shipToLogger({ logLevel, messages, data, hasData }) { if (this.config.enabled === false) { return messages; } const entry = { id: this.generateId(), timestamp: Date.now(), level: logLevel, message: messages.join(" "), data: hasData ? JSON.stringify(data) : null }; this.renderer.renderLogLine(entry); return messages; } /** * Changes the view mode for log display. * * @param viewMode - The new view mode to use */ setViewMode(viewMode) { if (this.config.enabled === false) { return; } const theme = this.config.theme || moonlight; const runtime = this.config.runtime; const viewConfig = { colors: { trace: _chalk2.default.gray, debug: _chalk2.default.blue, info: _chalk2.default.green, warn: _chalk2.default.yellow, error: _chalk2.default.red, fatal: _chalk2.default.bgRed.white, ...theme.colors }, logIdColor: theme.logIdColor || _chalk2.default.dim, dataValueColor: theme.dataValueColor || _chalk2.default.white, dataKeyColor: theme.dataKeyColor || _chalk2.default.dim }; this.renderer = new SimpleView({ viewMode, maxInlineDepth: this.config.maxInlineDepth || 4, showLogId: this.config.showLogId || false, timestampFormat: this.config.timestampFormat || "HH:mm:ss.SSS", collapseArrays: this.config.collapseArrays !== false, flattenNestedObjects: this.config.flattenNestedObjects !== false, runtime, includeDataInBrowserConsole: this.config.includeDataInBrowserConsole || false, config: viewConfig }); } /** * Gets the current view mode. * * @returns The current view mode */ getViewMode() { return this.renderer.getViewMode(); } }; // src/index.ts function getSimplePrettyTerminal(config) { return new SimplePrettyTerminalTransport(config); } exports.SimplePrettyTerminalTransport = SimplePrettyTerminalTransport; exports.getSimplePrettyTerminal = getSimplePrettyTerminal; exports.moonlight = moonlight; exports.nature = nature; exports.neon = neon; exports.pastel = pastel; exports.sunlight = sunlight; //# sourceMappingURL=index.cjs.map