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

ruki-logger

v1.8.1

Published

A colorful, location-aware logger inspired by the user's Flutter/Dart logger, for Node.js/TypeScript.

Readme

ruki-logger

A colorful,Configurable location‑aware logger.

  • Timestamps, level tags, and source file:line location
  • Pretty colors via chalk
  • Pluggable sinks via a LoggingRegistry (for piping logs to files, DBs, APMs, etc.)
  • Tiny, framework‑agnostic API mirroring your Dart methods: log, error, debug, test, highlight, warn, info, task, table, quiet, silent, custom(hex)

Install

npm i ruki-logger
# or
pnpm add ruki-logger
# or
yarn add ruki-logger

Quick start

import { Logger, LoggingRegistry, LogLevel } from "ruki-logger";

// optional: subscribe to log events (for piping elsewhere)
LoggingRegistry.addSink((level, payload) => {
  // e.g., forward to Datadog, write to file, etc.
  // console.debug("[sink]", level, payload);
});

Logger.info("Hello from ruki-logger");
Logger.warn("Careful!");
Logger.error(new Error("Boom"));
Logger.debug("Debug details");
Logger.task("Build completed");
Logger.highlight("Important");
Logger.test("Only during tests? up to you!");
Logger.custom("Any hex color works", "#9b59b6");
Logger.table(
  [
    { id: 1, name: "Alice", score: 92 },
    { id: 2, name: "Bob", score: 81 },
  ],
  { tag: "STATS", label: "Leaderboard", hideTimestamp: false },
);
Logger.silent("Emit to sinks without console noise");
Logger.info("User payload", { id: 42, name: "Jane" }, { tag: "API" }); // console.log-style varargs

Logger.silent will emit to all registered sinks but skip console output; Logger.quiet does the reverse (console only, no sinks). Logger.table prints timestamp/location header followed by a console table; when silent: true, it only emits a CSV payload to sinks.

Configuration examples

// 1. Default (relative file paths, timestamp hidden)
Logger.info("Server ready");

// 2. Show ISO timestamp and absolute path
Logger.warn("Disk space low", {
  hideTimestamp: false,
  timestampFormat: "iso",
  locationPath: "absolute",
});

// 3. Use locale timestamp and hide location
Logger.info("Cache warmed", {
  hideTimestamp: false,
  timestampFormat: "locale",
  showLocation: false,
});

// 4. Relative time (timeago) between logs
Logger.task("Transcoding complete", {
  hideTimestamp: false,
  timestampFormat: "timeago",
});

// 5. Custom timestamp formatter + custom tag
Logger.custom(
  "Webhook delivered",
  "#9b59b6",
  {
    tag: "WEBHOOK",
    hideTimestamp: false,
    timestampFormat: (now) => now.toUTCString(),
    colorOnlyTag: true,
  }
);

// 6. Custom formatter (location before message)
Logger.info("Formatter demo", {
  format: "#4%##2%####4%###",
  hideTimestamp: false,
});

// 7. Decorators & color overrides
Logger.warn("Decorated tag warning", {
  tagDecorator: "{}{}",
  colorOptions: {
    tag: "#ff6b6b",
    message: "#ffeaa7",
  },
  hideTimestamp: false,
});

// 8. Fixed-width columns
Logger.info("Aligned columns", {
  hideTimestamp: false,
  cellSizes: {
    timestamp: { min: 25 },
    tag: { min: 10 },
    message: { min: 24, max: 42 },
    location: { min: 28 },
  },
});

// 9. Override default colors per level
Logger.configure({
  levelColors: {
    info: "#55efc4",
    error: "#e74c3c",
  },
});

// 10. Level tagging inside message body
Logger.info("Indexed 2k docs", {
  enableLevelTagging: true,
  tag: "DB",
});

// 11. Custom level tags/colors (foreground + background)
Logger.info("Level badges your way", {
  enableLevelTagging: true,
  levelTaggingOptions: {
    info: { tag: "I", color: "#0ff", bgColor: "#111" },
    error: { tag: "ERR", bgColor: "#2b0000" }, // fg defaults to level color
  },
});

// 12. Force colors when your runtime strips ANSI (Nest CLI/PM2/CI)
Logger.configure({ forceColorLevel: true });
Logger.warn("Colors stay enabled", { forceColorLevel: 3 });

API

LoggerOptions

type LoggerOptions = {
  tag?: string;
  isDebug?: boolean;
  colorOnlyTag?: boolean;
  forceColorLevel?: 0 | 1 | 2 | 3 | boolean;
  leftSymbol?: string;
  rightSymbol?: string;
  showLocation?: boolean;
  locationPath?: "relative" | "absolute";
  hideTimestamp?: boolean;
  timestampFormat?:
    | "iso"
    | "locale"
    | "time"
    | "date"
    | "timeago"
    | ((timestamp: Date) => string);
  /**
   * `#`=timestamp, `##`=tag, `###`=message, `####`=location.
   * Numbers before `%` describe spaces between each segment.
   */
  format?: string;
  /** wrap the tag in these characters (default: "[]") */
  tagDecorator?: string;
  colorOptions?: {
    timestamp?: string;
    tag?: string;
    message?: string;
    location?: string;
  };
  levelColors?: Partial<Record<LogLevel, string>>;
  cellSizes?: {
    timestamp?: { min?: number; max?: number };
    tag?: { min?: number; max?: number };
    message?: { min?: number; max?: number };
    location?: { min?: number; max?: number };
  };
  enableLevelTagging?: boolean;
  levelTaggingOptions?: {
    [level in LogLevel]?: {
      tag?: string;
      color?: string;
      bgColor?: string;
    };
  };
};
  • hideTimestamp defaults to true. Set it to false and pick any timestampFormat.
  • locationPath can be "relative" or "absolute", defaulting to relative paths.
  • format lets you rearrange timestamp/tag/message/location while specifying the spaces between each segment. Invalid strings fall back to #1%##1%###1%####.
  • tagDecorator wraps the tag with any characters (1 char mirrors, 2 chars become left/right, longer strings split evenly).
  • colorOptions can override the colors of each segment (timestamp/tag/message/location). By default, tag + message use the level color, timestamp is white, and location is gray.
  • forceColorLevel overrides Chalk's color detection (handy when Nest/PM2/CI disables ANSI colors). true = level 3, false = no colors.
  • levelTaggingOptions customizes the level badge when enableLevelTagging is on (per-level tag text, foreground color, and background color).
  • levelColors changes the default color per log level (affects tag/message defaults and level badge color when not overridden).
  • cellSizes enforces min/max widths for each segment so multiple log lines stay aligned (e.g., pad the tag to 10 chars, trim messages at 80).
  • enableLevelTagging adds the level alias (e.g., APP, NET) before the message content, spaced using the same padding helpers.

Custom formats

The format string only understands two tokens:

  • #, ##, ###, #### which render timestamp, tag, message, and location respectively (each must appear once).
  • <number>% which inserts that many spaces between two segments.

Examples:

#2%##4%###2%####  ->  2025-11-18T16:07:11.364Z  [WARNING]    Careful!  Location: example/index.ts:39
#4%##2%####4%###  ->  2025-11-18T16:07:11.364Z    [WARNING]  Location: example/index.ts:39    Careful!

If a referenced segment is hidden (e.g., timestamp or location), the surrounding spaces are automatically removed.

Global defaults

Call Logger.configure() once during startup to set project-wide defaults (you can still override per call):

Logger.configure({
  hideTimestamp: false,
  format: "#2%##4%###2%####",
  tagDecorator: "<>",
  colorOptions: {
    timestamp: "#9b59b6",
    location: "#7f8c8d",
  },
  cellSizes: {
    timestamp: { min: 25 },
    tag: { min: 10 },
    message: { min: 24 },
    location: { min: 30 },
  },
  enableLevelTagging: true,
});

Methods

All methods add ISO timestamp and call‑site location. Pass any number of message arguments (just like console.log); if you need LoggerOptions, provide them as the final argument. For Logger.custom, the last non-options argument is treated as the hex color.

Logger.log(...args: unknown[])
Logger.error(...args: unknown[])
Logger.test(...args: unknown[])
Logger.highlight(...args: unknown[])
Logger.warn(...args: unknown[])
Logger.info(...args: unknown[])
Logger.task(...args: unknown[])
Logger.custom(...args: unknown[]) // last non-options arg = color hex

LoggingRegistry

Simple pub/sub for log events.

type Sink = (level: LogLevel, payload: {
  message: string;
  location: string;
  timestamp: string;
  raw: unknown;
  tag: string | undefined;
}) => void;

LoggingRegistry.addSink(sink: Sink): () => void     // returns an unsubscribe fn
LoggingRegistry.removeSink(sink: Sink): void
LoggingRegistry.emit(level: LogLevel, payload: ...): void

Example

See example/index.ts. Run it with:

npm i
npm run build
node dist/example/index.js
# or directly with ts-node for dev
npx ts-node example/index.ts

TypeScript config

This package ships ESM ("type": "module"). Node 18+ recommended.

License

MIT