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

@kirklin/logger

v0.1.1

Published

A modern, flexible, and elegant logger for Node.js and the browser.

Readme

@kirklin/logger

npm version npm downloads license

A modern, flexible, and elegant logger for Node.js and the browser.

  • Elegant & Intuitive API: Works just like console.log but with levels and superpowers.
  • Structured & Raw Logging: Supports both structured field() logging for clarity and raw object logging for quick debugging.
  • Node.js & Browser Support: First-class, optimized experience in both environments.
  • Smart Browser Grouping: Intelligently groups logs in the browser to reduce clutter without hiding important information.
  • Named Loggers: Create sub-loggers with automatic, deterministic coloring to easily distinguish log sources.
  • Performance Timers: Simple time() utility to measure and log execution times.
  • Extensible: Easily create custom Transports and Formatters to fit your needs.
  • Lightweight & Typed: Written in TypeScript with zero dependencies.

Install

# pnpm
pnpm add @kirklin/logger

# yarn
yarn add @kirklin/logger

# npm
npm install @kirklin/logger

Getting Started

import { field, logger, time } from "@kirklin/logger";

// --- Basic Logging ---
logger.info("Hello from the playground! This is a standard info message.");
logger.warn("This is a warning message.");
logger.error("This is an error message.");
logger.debug("This is a debug message (won't show by default).");
logger.trace("This is a trace message (won't show by default).");

console.log("\n"); // Spacer

// --- Flexible Object & Argument Logging ---
logger.info("Logging a single object:", { id: 1, name: "Kirk Lin", status: "active" });
logger.info("Logging multiple objects:", { user: "kirk" }, { permissions: ["read", "write"] });
const myVar = { a: 1, b: [2, 3] };
logger.info("Logging a variable:", myVar);
logger.error("An error occurred", new Error("Something went wrong!"));

console.log("\n"); // Spacer

// --- Structured Logging with `field()` ---
logger.info("User logged in successfully", field("userId", 12345), field("tenant", "acme-corp"));

// --- Mixed Logging (message, fields, and raw objects) ---
logger.warn(
  "Request to deprecated endpoint",
  field("endpoint", "/api/v1/legacy"),
  { userAgent: "Mozilla/5.0" },
);

console.log("\n"); // Spacer

// --- Named Loggers ---
const dbLogger = logger.named("database");
const apiLogger = logger.named("api");
dbLogger.info("Connecting to the database...");
apiLogger.info("Incoming request to /users");

console.log("\n"); // Spacer

// --- Timers ---
const timer = time(200);
setTimeout(() => {
  dbLogger.info("Database connected successfully", field("time", timer));
}, 150);

const failingTimer = time(100);
setTimeout(() => {
  apiLogger.error("Request timed out", field("time", failingTimer));
}, 200);

// --- Lazy Evaluation ---
logger.debug(() => {
  // This expensive operation will only be performed if the log level is debug or lower
  const complexData = { a: 1, b: Array.from({ length: 1e6 }, (_, i) => i).reduce((a, b) => a + b, 0) };
  return ["This is a debug message with complex data", field("data", complexData)];
});

// --- Throttling ---
logger.info("\n--- Testing Throttling (you should only see a few messages below) ---");
for (let i = 0; i < 100; i++) {
  logger.info("This is a spam message");
}

Features

Logging Arguments

You can pass any number of arguments of any type, just like console.log.

logger.info("Simple string message");
logger.error("An error occurred", new Error("File not found"));
logger.warn("Request to deprecated endpoint", {
  url: "/api/v1/legacy",
  statusCode: 410,
});

Named Loggers

Create context-specific loggers that are automatically colored and tagged.

const dbLogger = logger.named("database");
const apiLogger = logger.named("api");

dbLogger.info("Connecting to PostgreSQL...");
// [database] [info] Connecting to PostgreSQL...

apiLogger.info("GET /users request received");
// [api] [info] GET /users request received

Timers

Easily measure the duration of operations.

import { field, logger, time } from "@kirklin/logger";

const timer = time(100); // Optional: expected duration in ms for color coding

setTimeout(() => {
  // This will be colored green because it finished within the expected time
  logger.info("Data fetched", field("time", timer));
}, 80);

const slowTimer = time(50);
setTimeout(() => {
  // This will be colored red because it exceeded the expected time
  logger.warn("Task finished late", field("time", slowTimer));
}, 120);

Lazy Evaluation

For performance-critical logs, you can pass a function. The function will only be executed if the corresponding log level is enabled, avoiding unnecessary computations.

logger.debug(() => {
  const expensiveData = someComplexCalculation();
  return ["Debug data calculated", field("data", expensiveData)];
});

Extensibility

You can easily extend the logger by creating your own Transport or Formatter.

Custom Transport

A transport is responsible for sending the log to a destination (e.g., console, file, remote service).

import type { LogObject, Transport } from "@kirklin/logger";

class MyFileTransport implements Transport {
  log(logObject: LogObject) {
    const formattedMessage = myCustomFormatter.format(logObject);
    // Logic to write `formattedMessage` to a file...
  }
}

// Then add it to the logger instance
logger.transports.push(new MyFileTransport());

Custom Formatter

A formatter is responsible for converting a LogObject into a string or another printable format.

import type { LogObject, MessageFormat } from "@kirklin/logger";
import { Formatter } from "@kirklin/logger";

class MyJSONFormatter extends Formatter {
  format(logObject: LogObject): MessageFormat {
    // In a real implementation, you would return a format string and args.
    // For simplicity, we just stringify the object here.
    const jsonString = JSON.stringify({
      timestamp: logObject.date,
      level: logObject.level,
      message: logObject.message,
      ...logObject.args,
    });
    return { format: "%s", args: [jsonString], fields: [] };
  }
}

License

MIT License © 2022-PRESENT Kirk Lin