UNPKG

caterpillar

Version:

Caterpillar is the ultimate logging system for Deno, Node.js, and Web Browsers. Log levels are implemented to the RFC standard. Log entries can be filtered and piped to various streams, including coloured output to the terminal, the browser's console, and

177 lines (151 loc) 4.47 kB
// Imports import { LogEntry } from '../logger.ts' import { Transform } from '../transform.ts' import { inspect } from 'node:util' import * as ansi from 'https://unpkg.com/@bevry/ansi@^6.9.0/edition-deno/index.ts' /** * Return the given argument. * Used for when there is no formatter. */ function ansiNoop(a: string): string { return a } /** A mapping of log level numbers to their intended colours */ interface LevelsToColorsMap { [logLevelNumber: string]: ansi.ANSIApplier } /** Configuration options for the Caterpillar Human Transform */ export interface HumanOptions { /** Use to override the default value of {@link Human.color} */ color?: boolean /** Use to override the default value of {@link Human.colors} */ colors?: LevelsToColorsMap } /** * Convert Logger entries into human readable format. * @extends Transform * @example * ``` javascript * import { Logger, Human } from 'caterpillar' * const logger = new Logger() * const human = new Human() * logger.pipe(human).pipe(process.stdout) * logger.log('info', 'some', {data: 'oh yeah'}, 42) * ``` */ export class Human extends Transform { /** Whether or not to use colors? */ public color: boolean = true /** Mapping of which log level numbers correspond to which colours */ public colors: LevelsToColorsMap = { '0': 'red', '1': 'red', '2': 'red', '3': 'red', '4': 'yellow', '5': 'yellow', '6': 'green', '7': 'green', } /** Create our instance and apply our configuration options. */ constructor(opts?: HumanOptions) { super() // options if (opts?.color != null) this.color = opts.color if (opts?.colors != null) this.colors = opts.colors } /** Get the color for the log level */ getColor(levelNumber: number): ansi.ANSIApplier | false { // Determine const color = this.colors[levelNumber] || false // Return return color } /** Pad the left of some content if need be with the specified padding to make the content reach a certain size */ padLeft(padding: string, size: number, content: string | number): string { // Prepare padding = String(padding) content = String(content) // Handle if (content.length < size) { for (let i = 0, n = size - content.length; i < n; ++i) { content = padding + content } } // Return return content } /** Convert logger entry arguments into a human readable string */ formatArguments(args: any[]): string { return args .map((value) => typeof value === 'string' ? value : inspect(value, { showHidden: false, depth: 10, colors: this.color, }) ) .join(' ') } /** Convert a datetime into a human readable format */ formatDate(datetime: Date | number | string): string { // Prepare const now = new Date(datetime) const year = now.getFullYear() const month = this.padLeft('0', 2, now.getMonth() + 1) const date = this.padLeft('0', 2, now.getDate()) const hours = this.padLeft('0', 2, now.getHours()) const minutes = this.padLeft('0', 2, now.getMinutes()) const seconds = this.padLeft('0', 2, now.getSeconds()) const ms = this.padLeft('0', 3, now.getMilliseconds()) // Apply const result = `${year}-${month}-${date} ${hours}:${minutes}:${seconds}.${ms}` // Return return result } /** Convert a logger entry into a human readable format */ format(entry: LogEntry): string { // Prepare const { color } = this const useLine = entry.line !== -1 let result: string // Format const format = { color: this.getColor(entry.levelNumber), timestamp: this.formatDate(entry.date), text: this.formatArguments(entry.args), } // Check if (format.text) { // Formatters const levelFormatter = (color && format.color && ansi[format.color]) || ansiNoop const lineFormatter = (useLine && color && ansi.dim) || ansiNoop // Message // @ts-ignore const levelString = levelFormatter(`${entry.levelName}:`) const entryString = format.text const messageString = `${levelString} ${entryString}` // Format if (useLine) { // Line Information const seperator = '\n ' const debugString = lineFormatter( `→ [${format.timestamp}] [${entry.file}:${entry.line}:${entry.char}] [${entry.method}]` ) // Result result = `${messageString}${seperator}${debugString}\n` } else { // Result result = `${messageString}\n` } } else { result = format.text } // Return return result } } export default Human