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

plainlog

v1.0.0

Published

A lightweight, pluggable, and testable logger for modern apps (Node + browser).

Downloads

90

Readme

Logger Kit

A lightweight, pluggable, and testable logger for modern JavaScript and TypeScript apps — designed for real codebases, not just demos.

Logger Kit works in Node.js and the browser, supports context inheritance, buffering, transport safety, and is intentionally small enough that you can actually understand it.


Why Logger Kit Exists

Most popular loggers fall into one of two camps:

❌ Heavy & Abstract

  • Large APIs with many concepts
  • Hard to reason about in tests
  • Context propagation is awkward or missing
  • Easy to misuse, hard to debug

❌ Ultra-fast but Rigid

  • Optimized for throughput over ergonomics
  • Minimal context support
  • Not great for testing or small tools
  • Often feels “too low-level” for application logic

Logger Kit’s Philosophy

Logger Kit is built around a simple idea:

Logging should be easy to reason about, easy to test, and never break your app.

That means:

  • No global singletons
  • No magic context
  • No hidden async behavior
  • No logging failures crashing your process
  • No framework lock-in

Logger Kit is especially well-suited for:

  • application code
  • CLIs and scripts
  • browser apps
  • test environments
  • distributed systems glue code

Features

Core

  • Log levels: debug, info, warn, error
  • Structured log entries with ISO timestamps
  • Context inheritance (withContext, child)
  • Fire-and-forget logging or awaitable logging

Safety & Reliability

  • Transport isolation (one bad transport won’t break others)
  • Timeout-protected transports
  • Centralized error handler with context
  • Optional buffering with flush semantics
  • Configurable buffer limits

Testability

  • Test mode to capture logs in memory
  • Deterministic async APIs for assertions
  • No globals or hidden state

Extensibility

  • Pluggable transports
  • Optional lifecycle hooks (flush, close)
  • Meta sanitization hook to prevent leaks
  • Simple transport factory helper

Environment Support

  • Works in Node.js and the browser
  • Browser-safe core + transports
  • Node-only transports exported separately

Installation

npm install logger-kit

Basic Usage

import { Logger } from "logger-kit";

const logger = new Logger("info");

logger.info("App started");
logger.warn("Low disk space", { freeMB: 120 });
logger.error("Something broke", { code: "E_FAIL" });

Logs are structured objects internally and only formatted by transports.


Transports

Console (browser & Node)

import { ConsoleTransport } from "logger-kit/transports";

logger.use(new ConsoleTransport());

JSON Console

import { JsonConsoleTransport } from "logger-kit/transports";

logger.use(new JsonConsoleTransport());

Silent (useful for tests)

import { SilentConsoleTransport } from "logger-kit/transports";

logger.use(new SilentConsoleTransport());

Node-Only Transports

import { FileTransport } from "logger-kit/transports/node";

logger.use(new FileTransport("./app.log"));

Other node transports:

  • ProcessStreamTransport
  • IpcTransport
  • SmartFileTransport

Context Inheritance

Logger Kit supports explicit context propagation.

const base = new Logger("info", { service: "api" });

const requestLogger = base.withContext({ reqId: "abc123" });

requestLogger.info("Handling request");

Replace vs Merge Context

logger.withContext({ a: 1, b: 2 });
logger.withContext({ b: 9 }, "replace");

child() Alias

const child = logger.child({ userId: "u1" });

Fire-and-Forget vs Awaitable Logging

By default, logging is fire-and-forget:

logger.info("Hello"); // no await needed

If you need determinism (tests, shutdown, error handling):

await logger.infoAsync("Hello");

Available async variants:

  • debugAsync
  • infoAsync
  • warnAsync
  • errorAsync
  • logAsync

Buffering & Flushing

Useful for startup, batch jobs, or controlled output.

logger.enableBufferMode();

logger.info("Buffered");
logger.info("Still buffered");

await logger.flush(); // writes all buffered logs

Buffers are capped to prevent memory leaks.


Test Mode

Capture logs without a transport:

logger.enableTestMode();

await logger.warnAsync("Rate limited");

expect(logger.testLogs()).toEqual([
  expect.objectContaining({ level: "warn" })
]);

Test logs are isolated and capped.


Error Handling

Transport failures never throw.

Instead, you can register a handler:

logger.setErrorHandler((err, { transport, entry }) => {
  // report to Sentry, metrics, etc.
});

Timeouts are treated as errors too.


Meta Sanitization

Prevent secrets from leaking into logs:

logger.setSanitizer((meta) => {
  const { token, password, ...safe } = meta;
  return safe;
});

Runs for:

  • normal logging
  • raw log injection
  • all transports

Custom Transports

Implement a Transport

import type { Transport, LogEntry } from "logger-kit";

class MyTransport implements Transport {
  async log(entry: LogEntry) {
    await sendSomewhere(entry);
  }

  async flush() {}
  async close() {}
}

Or use createTransport()

import { createTransport } from "logger-kit";
import { prettyFormatter } from "logger-kit/formatter";

const transport = createTransport(console.log, prettyFormatter);
logger.use(transport);

createLogger() Helper

For quick setup with sane defaults:

import { createLogger } from "logger-kit";

const logger = createLogger({
  level: "info",
});

Supports:

  • env-based LOG_LEVEL
  • default console transport
  • custom transports
  • buffer limits

API Summary

Logger

  • debug | info | warn | error
  • debugAsync | infoAsync | warnAsync | errorAsync
  • withContext, child
  • enableBufferMode, flush, close
  • enableTestMode, testLogs
  • setLevel, setErrorHandler, setSanitizer
  • log, logAsync

Transport

  • log(entry)
  • flush?()
  • close?()

License

MIT