npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

Readme

jk-log

npm version license bundle size tests

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-log

Quick 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.js

Check 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