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

@khanprog/logger

v0.5.0

Published

Universal colored console logger for Node.js and the browser (ESM, CJS, TypeScript)

Readme

@khanprog/logger

A lightweight universal console logger for Node.js and the browser, with ESM / CommonJS / TypeScript support.

Works with pnpm, npm, yarn, and bun. Dual Node + browser builds with full TypeScript declarations.

Table of contents

Why @khanprog/logger?

👌 Easy to use — createLogger() and go

💅 Colored pretty output with icons, plus machine-readable JSON mode

🌐 Universal — Node.js and the browser (dual builds)

📘 TypeScript-first with full declarations (ESM + CJS)

🏷 Tag / context support via withTag and child

✨ Semantic DX helpers — success, fail, start, ready

🎚️ Level filtering (debug → error) with env override (KP_LOG_LEVEL)

🎨 Themeable icons and colors (named or raw ANSI)

📄 Project config via kp-logger.json / .js / .ts (or in-memory config)

⏱️ Built-in timeIt timing, circular-safe safeStringify, and measureSize helpers

🪶 Zero runtime dependencies

Every log line includes an ISO timestamp ([2024-06-06T12:34:56.789Z]). Example times below are illustrative. Examples use colors: false so output is easy to read.

Install

pnpm add @khanprog/logger
# or: npm / yarn / bun

Recommended usage

Prefer createLogger for application code. Use a bound context (or withTag) for module scope, core levels for severity, and semantic helpers for DX milestones.

import { createLogger } from "@khanprog/logger";

const log = createLogger({
  context: "App",
  level: "info",
  colors: false,
});

log.info("Server started");
log.warn("Cache miss", { meta: { key: "user:1" } });
log.error("Request failed", { meta: new Error("timeout") });

log.success("Deploy complete");
log.fail("Migration aborted");
log.start("Building…");
log.ready("Listening on :3000");

const auth = log.withTag("Auth"); // → [App:Auth]
auth.info("token verified", { meta: { userId: 1 } });

const done = log.timeIt("boot", { level: "debug" });
done();

Output:

[2024-06-06T12:34:56.789Z] ℹ️ [INFO] [App] Server started
[2024-06-06T12:34:56.790Z] ⚠️ [WARN] [App] Cache miss {"key":"user:1"}
[2024-06-06T12:34:56.791Z] ❌ [ERROR] [App] Request failed {"__type":"Error","name":"Error","message":"timeout","stack":"..."}
[2024-06-06T12:34:56.792Z] ✅ [SUCCESS] [App] Deploy complete
[2024-06-06T12:34:56.793Z] 💥 [FAIL] [App] Migration aborted
[2024-06-06T12:34:56.794Z] 🚀 [START] [App] Building…
[2024-06-06T12:34:56.795Z] ✨ [READY] [App] Listening on :3000
[2024-06-06T12:34:56.796Z] ℹ️ [INFO] [App:Auth] token verified {"userId":1}
[2024-06-06T12:34:56.797Z] 🔍 [DEBUG] [App] 🚀 ~ Execution time for "boot": 0.420ms

Frontend & backend

| Environment | How to configure | | ------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Browser / Vite / Next / CRA | Pass config or options to createLogger() (no filesystem). Bundlers resolve the browser build automatically. | | Node.js / Bun | Auto-load kp-logger.ts / .js / .json, or pass config / options the same way. |

import { createLogger } from "@khanprog/logger";
import config from "./kp-logger.json";

const log = createLogger({ config, colors: false });
log.info("hello");

Core levels

Rank order: debug < log < info < warn < error.

Methods: .debug, .log, .info, .warn, .error.

Each method: (message?: unknown, fields?: LogFields).

Arguments

| Call | Meaning | |------|---------| | log.info(msg) | message only | | log.info(msg, { meta }) | message + structured data | | log.info(msg, { context }) | one-off context override | | log.info(msg, { meta, context }) | both | | log.withTag(tag).info(msg, { meta }) | preferred scoping |

const log = createLogger({ colors: false, configFile: false });

log.debug("detail");
log.log("plain");
log.info("ok");
log.warn("careful");
log.error("failed");
[2024-06-06T12:34:56.789Z] 🔍 [DEBUG] detail
[2024-06-06T12:34:56.790Z] 📝 [LOG] plain
[2024-06-06T12:34:56.791Z] ℹ️ [INFO] ok
[2024-06-06T12:34:56.792Z] ⚠️ [WARN] careful
[2024-06-06T12:34:56.793Z] ❌ [ERROR] failed

Level filtering

const log = createLogger({ level: "warn", colors: false, configFile: false });

log.debug("nope"); // skipped
log.info("nope");  // skipped
log.warn("yes");
log.error("yes");

Scoping

Bound context

const log = createLogger({ context: "Auth", colors: false, configFile: false });
log.info("token verified");
// → ℹ️ [INFO] [Auth] token verified

withTag (recommended) and child

withTag(tag) is an alias for child({ context: tag }). Contexts join with :.

const app = createLogger({ context: "App", colors: false, configFile: false });
const auth = app.withTag("Auth");
auth.info("ok");
// → ℹ️ [INFO] [App:Auth] ok

// Full options escape hatch:
const login = auth.child({ context: "Login", level: "debug" });

Per-call context override

Prefer withTag for ongoing scope. Use fields.context for a one-off override:

const log = createLogger({ context: "Auth", colors: false, configFile: false });
log.info("ok", { context: "Login" });
// → ℹ️ [INFO] [Login] ok

Structured meta

Pass structured data under fields.meta (never as a bare second object):

log.info("user created", { meta: { userId: 42, role: "admin" } });
// → ℹ️ [INFO] user created {"userId":42,"role":"admin"}

log.info("ok", { meta: { userId: 1 }, context: "Auth" });
// → ℹ️ [INFO] [Auth] ok {"userId":1}

Semantic helpers

Semantic DX methods. They keep lean filter levels (LogLevel is unchanged) and use distinct icons/labels.

| Method | Filters as | Label | | --------- | ---------- | --------- | | success | info | ✅ SUCCESS | | fail | error | 💥 FAIL | | start | info | 🚀 START | | ready | info | ✨ READY |

In JSON mode, output includes both "level" (filter rank) and "kind" (success | fail | start | ready).

const log = createLogger({ format: "json", configFile: false });
log.success("done");
// → {"time":"...","level":"info","kind":"success","message":"done"}

Timing & utils

timeIt

Browser-safe timing via performance.now() (falls back to Date.now()). Options are named: { context?, level? }. Default level: debug.

const log = createLogger({ colors: false, configFile: false });

const done = log.timeIt("fetchData");
// ... work ...
done();
// → 🔍 [DEBUG] 🚀 ~ Execution time for "fetchData": 12.345ms

const done2 = log.timeIt("fetchData", {
  context: "UserService",
  level: "warn",
});
done2();
// → ⚠️ [WARN] [UserService] 🚀 ~ Execution time for "fetchData": 12.345ms

Named export (uses the package default logger):

import { timeIt } from "@khanprog/logger";
const done = timeIt("boot");
done();

safeStringify

Circular-safe JSON helper (Error, Map, Set, Date, BigInt, functions, typed arrays).

import { safeStringify } from "@khanprog/logger";

const obj: Record<string, unknown> = { name: "kp" };
obj.self = obj;
console.log(safeStringify(obj, 0));
// → {"name":"kp","self":"[Circular]"}

measureSize

Measure the byte size of any value — plain text, binary, or structured data.

| Input | How size is measured | | --- | --- | | string | UTF-8 length as-is (no JSON quotes) | | ArrayBuffer / typed arrays / Buffer | .byteLength | | primitives | UTF-8 length of String(value) | | objects / arrays | Compact safeStringify, then UTF-8 length |

import { createLogger, formatBytes, isWithinSizeLimit, measureSize } from "@khanprog/logger";

const body = { foo: "bar" };
const size = measureSize(body);
// → { bytes: 13, kb: "0.01", mb: "0.00" }

measureSize("hello").bytes; // 5 (not JSON-quoted 7)
measureSize(new Uint8Array(4)).bytes; // 4

formatBytes(size.bytes); // "13 B"
isWithinSizeLimit(body, 1024 * 1024); // true if <= 1 MB

const log = createLogger({ colors: false, configFile: false });
log.info("response received", {
  meta: { size: formatBytes(size.bytes) },
});

Config

createLogger(options?)

| Option | Type | Default | Description | | ------------- | ----------------------------------- | ------------------ | -------------------------------------------------------------------------------- | | level | LogLevel | "debug" | Minimum level (debug < log < info < warn < error) | | context | string | "" | Bound context label on every line | | colors | "auto" | boolean | "auto" | ANSI colors (NO_COLOR / FORCE_COLOR / TTY aware; off in browsers by default) | | format | "pretty" | "json" | "pretty" | Human or machine-readable output | | icons | Partial<Record<LogLevel, string>> | built-ins | Override per-level icons | | levelColors | Partial<Record<LogLevel, string>> | built-ins | Override per-level colors (named or ANSI) | | config | KpLoggerFileConfig | — | In-memory config (browser + Node) | | configFile | string | false | auto kp-logger.* | Filesystem path (Node only), or false to skip |

Merge order: built-in defaults → kp-logger.* file (Node) → config object → createLogger fields.

Config files (kp-logger.json / .js / .ts)

Browser: import the file and pass it via config.

Node: place the file in the project root (or a parent). Discovery order:

kp-logger.ts → .mts → .cts → .js → .mjs → .cjs → .json

TypeScript ([kp-logger.example.ts](kp-logger.example.ts)):

import { defineConfig } from "@khanprog/logger";

export default defineConfig({
  level: "info",
  icons: { info: "💙", error: "🔥" },
  levelColors: { info: "blue", error: "brightRed" },
});

JSON ([kp-logger.example.json](kp-logger.example.json)):

{
  "level": "info",
  "colors": "auto",
  "format": "pretty",
  "context": "App",
  "icons": {
    "info": "💙",
    "warn": "🧡",
    "error": "🔥",
    "debug": "🐛",
    "log": "📄"
  },
  "levelColors": {
    "info": "blue",
    "warn": "yellow",
    "error": "brightRed",
    "debug": "magenta",
    "log": "white"
  }
}

Named colors: black, red, green, yellow, blue, magenta, cyan, white, gray, plus brightRed, brightCyan, etc. Raw ANSI such as "\\x1b[36m" also works.

On Node, .ts configs need a TypeScript loader (e.g. tsx) or Node type-stripping. Prefer .json / .cjs for plain node.


Formats & colors

Pretty (default)

[2024-06-06T12:34:56.789Z] ℹ️ [INFO] [App] Server started

JSON

const log = createLogger({ format: "json", context: "API", configFile: false });
log.info("handled", { meta: { status: 200 } });
{"time":"2024-06-06T12:34:56.789Z","level":"info","message":"handled","context":"API","meta":{"status":200}}

Color behavior

| Condition | Result | | ----------------- | --------- | | colors: false | Off | | colors: true | On | | NO_COLOR set | Off | | FORCE_COLOR set | On | | Node TTY | On (auto) | | Browser (auto) | Off |

JSON format never uses ANSI colors.


Compat

Secondary API for quick scripts. Prefer createLogger for new application code.

import logger, { logger as log } from "@khanprog/logger";

logger("info", "hello", { context: "App" });
logger.info("also works");
log.success("named export");
logger.child({ context: "X" }).warn("hi");

Env: set KP_LOG_LEVEL (e.g. warn) to raise the default logger’s minimum level.

KP_LOG_LEVEL=warn node app.js

CommonJS

const { createLogger, logger, measureSize, safeStringify } = require("@khanprog/logger");

const log = createLogger({ colors: false, configFile: false });
log.info("from cjs");

Types

import type {
  ByteSize,
  LogLevel,
  LogKind,
  LogFields,
  TimeItOptions,
  Logger,
  LoggerOptions,
} from "@khanprog/logger";

License

MIT