jk-log
v2.1.2
Published
A tiny Node.js utility that enhances console.log with ANSI colors and formatting, making terminal output cleaner, more readable, and visually expressive with minimal setup.
Maintainers
Readme
jk-log
A tiny, zero-dependency Node.js utility that enhances console output with ANSI colors and formatting. Includes a feature-rich logger, an ANSI string builder, and pluggable writers for console, file, and HTTP (JSON-RPC) output.
Installation
npm install jk-logQuick examples
import { logger, styled, createLogger } from "jk-log";
logger.log("Hello world!");
logger.yellow("A warning message");
logger.red("Error: file not found");
logger.green.bold("Success!");
// styled returns an ANSI string
console.log(styled.hex("#ff00aa").underline("Custom color"));
// styled builds a string you can manipulate or combine
const s = styled.bold.rgb(0, 128, 255)("Info:") + " " + styled.underline("details");
console.log(s);
logger.rgb(255, 0, 0)("Red via RGB");
logger.bgBlue.white("White text on blue background");Log levels
Seven levels (lowest to highest): trace, debug, info, warn, error, fatal, silent. Default: info.
logger.trace("Won't print either");
logger.debug("Won't print at default level");
logger.info("Informational message");
logger.warn("Warning");
logger.error("Error");
logger.fatal("Critical failure — system shutting down");Set level to "silent" to suppress all output:
const log = createLogger({ logLevel: "silent", writers: [consoleWriter()] });
log.error("This won't print");Change at runtime or via LOG_LEVEL env var:
logger.setLevel("debug");
logger.debug("Now this prints");LOG_LEVEL=debug node app.jsCheck if a level is enabled
Use isLevelEnabled() to skip expensive string formatting when a level is suppressed:
if (logger.isLevelEnabled("debug")) {
logger.debug("Payload:", JSON.stringify(hugeObject));
}Custom loggers with createLogger
import { createLogger } from "jk-log";
const log = createLogger({
showTime: true, // ISO timestamp prefix
format: "json", // "plain" (default) or "json"
logLevel: "debug", // minimum log level
levelColors: { error: "magenta" },
levelLabels: { info: "[ℹ]", warn: "[⚠]" },
metadata: { app: "my-server", env: "production" }, // included in every entry
});
log.info("Server started on port 3000");
log.error("Something went wrong", { code: 500 });JSON format
When format: "json":
{
"level": "info",
"timestamp": "2026-05-05T12:00:00.000Z",
"message": "Server started",
"app": "my-server"
}Child loggers
Create a derived logger with merged metadata:
const reqLog = log.child({ requestId: "abc-123" });
reqLog.info("Handling request");Flushing and destroying
Flush pending buffered data across all writers (e.g., before process exit):
log.flush();Destroy all writers (flush + release resources like file descriptors and HTTP agents):
log.destroy();Inline style options for logger.log
logger.log("Styled!", { color: "red", modifiers: "bold" });
logger.log("Custom", { hex: "#ff8800", modifiers: ["underline", "italic"] });
logger.log("Background", { bgColor: "bgCyan" });Writers
Writers route log output to different destinations. Pass them via the writers option in createLogger. Each writer can have its own logLevel override and a destroy() method for cleanup.
consoleWriter
import { createLogger, consoleWriter } from "jk-log";
const log = createLogger({
writers: [consoleWriter({ methodMapping: { warn: "error" } })],
});| Option | Type | Default | Description |
| --------------- | ------ | ------- | ----------------------------------------------------- |
| methodMapping | object | {} | Map log levels to console methods (e.g. warn→error) |
| logLevel | string | — | Override the logger's minimum level for this writer |
fileWriter
Buffered file output with automatic flushing and log rotation.
import { createLogger, fileWriter } from "jk-log";
const log = createLogger({
writers: [fileWriter({ filePath: "./app.log", stripAnsi: true })],
});
log.info("Logged to a file");
log.error("Error details", new Error("fail"));
// Flush and close when done
log.destroy();Log rotation
Rotate log files by size, time interval, or both:
const log = createLogger({
writers: [
fileWriter({
filePath: "./app.log",
maxFileSize: 10 * 1024 * 1024, // 10 MB
maxFiles: 5, // keep app.1.log … app.5.log
rotationInterval: 86_400_000, // rotate daily (24h in ms)
}),
],
});Rotated files are named app.1.log, app.2.log, …, where .1 is the most recent. Files without an extension use app.1, app.2, etc.
| Option | Type | Default | Description |
| ------------------ | ------- | ------- | -------------------------------------------------------------- |
| filePath | string | — | Path to the log file (required) |
| stripAnsi | boolean | true | Strip ANSI sequences before writing |
| overwrite | boolean | false | Overwrite file on open instead of appending |
| logLevel | string | — | Override the logger's minimum level |
| bufferSize | number | 8192 | Bytes to buffer before flush (0 = no buffering) |
| maxFileSize | number | 0 | Max file size in bytes before rotating (0 = disabled) |
| maxFiles | number | 5 | Number of rotated backup files to keep |
| rotationInterval | number | 0 | Max age of the log file in ms before rotating (0 = disabled) |
httpWriter
Sends log entries as JSON-RPC 2.0 requests with batching and keep-alive.
import { createLogger, httpWriter } from "jk-log";
const log = createLogger({
writers: [
httpWriter({
url: "https://logs.example.com/rpc",
method: "log",
batchSize: 10,
flushInterval: 2000,
}),
],
});
log.info("Sent via HTTP");| Option | Type | Default | Description |
| --------------- | ------- | ------- | ---------------------------------------------- |
| url | string | — | JSON-RPC endpoint URL (required) |
| method | string | "log" | JSON-RPC method name |
| stripAnsi | boolean | true | Strip ANSI before sending |
| logLevel | string | — | Override the logger's minimum level |
| headers | object | {} | Additional HTTP headers |
| batchSize | number | 1 | Items before flush (1 = send immediately) |
| flushInterval | number | 5000 | Max ms to wait before flushing a partial batch |
| highWaterMark | number | 16384 | Byte threshold before flush (16 KB) |
| timeout | number | 30000 | Request timeout in ms |
| maxBufferSize | number | 10000 | Max buffered items before dropping entries |
API overview
| Export | Description |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| logger | Default logger instance (log level info). Proxy with all style methods, log, info, warn, error, fatal, debug, trace, setLevel, isLevelEnabled, flush, destroy. |
| styled | Callable ANSI string builder. Supports chaining: colors, backgrounds, modifiers, rgb/hex/bgRgb/bgHex. |
| createLogger(options?) | Creates a custom logger. Options: showTime, format, logLevel, levelColors, levelLabels, metadata, writers. |
| consoleWriter(options?) | Creates a console output writer. |
| fileWriter(options) | Creates a buffered file output writer with optional log rotation. |
| httpWriter(options) | Creates an HTTP JSON-RPC output writer. |
| shouldUseColor() | Returns boolean — whether color output is enabled. |
| stripAnsi(text) | Removes ANSI escape sequences from a string. |
Pretty-printing
In plain format, objects and arrays are formatted with util.inspect for readable output with colors, depth limiting, and circular-reference safety:
logger.info({ user: "Alice", roles: ["admin", "editor"] });
// [INFO] { user: 'Alice', roles: [ 'admin', 'editor' ] }JSON format (format: "json") is not affected — it continues to produce single-line JSON.
Color policy
- Set
NO_COLOR(any non-empty value) to disable colors. - Set
FORCE_COLOR=1(any non-empty value) to force colors. - By default, colors are enabled when stdout is a TTY.
Examples
See the ./examples/ directory for runnable scripts covering basic logging, styled output, JSON format, writers, child loggers, and more.
License
MIT — see LICENSE
