UNPKG

kliedz

Version:

Dead-simple, stateless logging utility for JavaScript and TypeScript. Pure functions. No dependencies. Just log.

93 lines (84 loc) 2.75 kB
import { inspect } from "node:util"; import type { Formatter, FormatterConfig } from "../types/formatter.js"; import { getColorFor, RESET_COLOR } from "./colors.js"; /** * Formats a log message with no colors. * Adds a prefix (with optional timestamp), followed by the joined message body. * * Example output: * `[INFO] message here` * or * `2025-05-10T12:34:56.789Z [INFO] message here` */ export const plainFormatter: Formatter = (config) => { const prefix = getPrefix(config); const body = config.args.map(formatArg).join(" "); return body ? `${prefix} ${body}` : prefix; }; /** * Formats a log message with ANSI colors based on log level. * Adds a prefix (with optional timestamp), wraps it in color codes. * * Example output: * `\x1b[33m[WARN] something happened\x1b[0m` */ export const colorFormatter: Formatter = (config) => { const color = getColorFor(config.level); const prefix = getPrefix(config); const body = config.args.map(formatArg).join(" "); const line = body ? `${prefix} ${body}` : prefix; return `${color}${line}${RESET_COLOR}`; }; /** * Formats various data types to be printed * * @param arg - Argument to format * @returns String ready to be printed */ export function formatArg(arg: unknown): string { if (typeof arg === "undefined") return "undefined"; if (arg instanceof Error) return `${arg.name}: ${arg.message}\n${arg.stack ?? ""}`; if (typeof arg === "string") return arg; if (typeof arg === "bigint") return `${arg}n`; if (typeof arg === "function") return `[Function ${(arg).name || "anonymous"}]`; if (typeof arg === "object" && arg !== null) { try { return JSON.stringify(arg, jsonReplacer); } catch { try { return inspect(arg, { depth: null, breakLength: Infinity }); } catch { return "[Unserializable Object]"; } } } return String(arg); } /** * Builds the log prefix for a given message. * If a custom `prefixBuilder` is provided in the config, it's used directly. * Otherwise, constructs `[LEVEL]` or `timestamp [LEVEL]` depending on config. * * @param config - Formatter config including log level and optional timestamp/custom builder. * @returns A formatted prefix string. */ export function getPrefix({ level, prefixBuilder, withTimestamp = false, }: FormatterConfig): string { if (typeof prefixBuilder === "function") { return prefixBuilder(); } const timestamp = withTimestamp ? `${new Date().toISOString()} ` : ""; return `${timestamp}[${level.toUpperCase()}]`; } function jsonReplacer(_k: string, v: unknown) { if (typeof v === "bigint") return String(v); if (typeof v === "symbol") return v.toString(); if (v instanceof Map) return Object.fromEntries(v); if (v instanceof Set) return Array.from(v); return v; }