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

@moonstar-x/logger

v2.0.0

Published

A tiny, zero-dependency logger for Node.js with levels, colors, timestamps, child loggers and JSON output.

Readme

@moonstar-x/logger

A tiny, zero-dependency logger for Node.js with levels, colors, timestamps, child loggers and JSON output.

  • Zero dependencies. Nothing but node:util and node:process.
  • Seven levels, from trace to fatal, with a silent threshold to turn everything off.
  • Pretty by default, JSON when you need it. One colored line per message for humans, one JSON object per line for log collectors.
  • Child loggers. Namespaced names and inherited structured bindings.
  • Configurable from the environment. LOG_LEVEL, LOG_FORMAT, LOG_TIMESTAMP, NO_COLOR and FORCE_COLOR are all honored out of the box.
  • Written in TypeScript, shipped as both ESM and CJS with full type declarations.

Installation

npm install @moonstar-x/logger

This package requires Node.js >=22.12.0.

Usage

The default logger

The package exports a ready-to-use logger, configured from the environment.

import logger from '@moonstar-x/logger';

logger.info('server listening on port 3000');
logger.warn('cache miss, falling back to the database');
logger.error(new Error('could not reach the database'));

CommonJS works too:

const { default: logger } = require('@moonstar-x/logger');

logger.info('server listening on port 3000');

Logger methods are not bound to their instance, so call them on the logger (logger.info(...)) instead of destructuring them (const { info } = logger).

Logging messages

Every method takes any number of arguments. Strings are used as-is, everything else is inspected, which means objects are serialized and errors keep their stack trace.

logger.trace('entering the request handler');           // gray,    stdout
logger.debug('resolved config', config);                // green,   stdout
logger.info('user signed in', { userId: 42 });          // cyan,    stdout
logger.log('plain message');                            // white,   stdout
logger.warn('token is about to expire');                // yellow,  stderr
logger.error(new Error('request failed'));              // red,     stderr
logger.fatal('unrecoverable, shutting down');           // magenta, stderr

Which prints:

(1:57:54 PM) - [TRACE] - entering the request handler
(1:57:54 PM) - [DEBUG] - resolved config { port: 3000 }
(1:57:54 PM) - [INFO] - user signed in { userId: 42 }
(1:57:54 PM) - [LOG] - plain message
(1:57:54 PM) - [WARN] - token is about to expire
(1:57:54 PM) - [ERROR] - Error: request failed
    at file:///app/index.js:12:14
    ...
(1:57:54 PM) - [FATAL] - unrecoverable, shutting down

You can also clear the terminal, which is a no-op when the output is not a TTY:

logger.clear();

Log levels

A logger only emits messages at or above its own level. log is an alias of info that keeps its own tag and color.

| Level | Severity | Method | Color | Destination | | -------- | -------- | ----------------- | ------- | ----------- | | trace | 10 | logger.trace() | gray | stdout | | debug | 20 | logger.debug() | green | stdout | | info | 30 | logger.info() | cyan | stdout | | info | 30 | logger.log() | white | stdout | | warn | 40 | logger.warn() | yellow | stderr | | error | 50 | logger.error() | red | stderr | | fatal | 60 | logger.fatal() | magenta | stderr | | silent | — | — | — | — |

The level can be read, set, or checked at any time:

logger.level;                      // 'info'
logger.level = 'debug';            // throws a TypeError on an invalid level
logger.setLevel('debug');          // same, but chainable

if (logger.isLevelEnabled('debug')) {
  logger.debug('expensive dump', buildExpensiveReport());
}

Creating a logger

createLogger builds a logger from the environment, with the options you pass taking precedence.

import { createLogger } from '@moonstar-x/logger';

const logger = createLogger({
  name: 'api',
  level: 'debug',
  format: 'pretty'
});

logger.info('ready');
// (1:57:54 PM) - [INFO] - (api) - ready

To ignore the environment entirely, construct the class directly:

import { Logger } from '@moonstar-x/logger';

const logger = new Logger({ level: 'warn' });

Child loggers

child() creates a logger that inherits its parent's configuration. Names are joined with a colon and bindings are merged, so subsystems are easy to tell apart.

const logger = createLogger({ name: 'api' });
const database = logger.child('db');
const pool = database.child({ name: 'pool', level: 'trace', bindings: { poolId: 1 } });

database.info('connected');
// (1:57:54 PM) - [INFO] - (api:db) - connected

pool.trace('acquired a connection');
// (1:57:54 PM) - [TRACE] - (api:db:pool) - acquired a connection

JSON output

Switching the format to json renders one JSON object per line, ready to be picked up by a log collector. This is where bindings come in: they are structured fields added to every entry.

const logger = createLogger({
  format: 'json',
  name: 'api',
  bindings: { service: 'checkout', version: '2.0.0' }
});

logger.info('order placed');
{"level":"info","time":"2026-09-10T18:57:54.678Z","name":"api","service":"checkout","version":"2.0.0","msg":"order placed"}

Bindings are serialized safely: circular references are replaced with [Circular], BigInt values are stringified, and errors are turned into their name, message and stack.

Custom destinations

Anything with a write method is a valid destination, which makes it trivial to point a logger at a file, a socket or an in-memory buffer — very handy in tests.

import fs from 'node:fs';

const file = fs.createWriteStream('app.log', { flags: 'a' });
const logger = createLogger({ format: 'json', stdout: file, stderr: file });
const lines = [];
const logger = createLogger({ stdout: { write: (line) => lines.push(line) }, color: false });

Configuration

Every option has a sensible default, so createLogger() is a valid call.

| Option | Type | Default | Description | |-------------|----------------------------------------------------------------|------------------|-------------------------------------------------------------------------------------------------| | bindings | Record<string, unknown> | {} | Structured fields added to every entry. Only rendered in json format. | | color | boolean | auto-detected | Forces colors on or off, bypassing NO_COLOR, FORCE_COLOR and TTY detection. | | depth | number \| null | null | How deep objects are inspected. null means unlimited. | | format | 'pretty' \| 'json' | 'pretty' | How the output is rendered. | | level | LogLevel | 'info' | The minimum severity that gets logged. | | name | string | — | A name shown on every entry, useful to tell subsystems apart. | | stderr | Destination | process.stderr | Where warn, error and fatal are written. | | stdout | Destination | process.stdout | Where trace, debug, info and log are written. | | timestamp | 'iso' \| 'time' \| ((date: Date) => string) \| false \| true | true | How the timestamp is rendered. true picks a locale time for pretty and ISO 8601 for json. |

Environment variables

createLogger and the default logger read these on startup. Explicit options always win.

| Variable | Effect | |-----------------|-------------------------------------------------------------------------| | LOG_LEVEL | The minimum severity, e.g. debug. Invalid values are ignored. | | LOG_FORMAT | json or pretty. | | LOG_TIMESTAMP | iso, time, or off / false to drop the timestamp. | | NO_COLOR | Disables colors, per the NO_COLOR convention. | | FORCE_COLOR | Forces colors on, even when the output is not a TTY. |

LOG_LEVEL=debug LOG_FORMAT=json node index.js

API

| Export | Description | |--------------------------------------------------------------------------|--------------------------------------------------------------------------| | logger (also the default export) | The default logger, configured from the environment. | | createLogger(options?) | Creates a logger from the environment, with options taking precedence. | | Logger | The logger class, for when the environment should be ignored. | | optionsFromEnvironment(environment?) | Reads logger defaults from an environment object. | | LOG_LEVELS, LEVEL_SEVERITY, LOG_METHODS | The level and method tables the logger is built on. | | isLogLevel(value), shouldLog(level, threshold) | Level helpers. | | formatPretty, formatJson, formatTimestamp, serializeArgs | The rendering primitives, exported for custom formatting. | | createStyler(isEnabled), isColorSupported(destination, environment?) | The ANSI helpers. |

Types are shipped with the package: LoggerOptions, LogFormat, LogLevel, LoggableLevel, LogMethod, MethodSpec, LogEntry, TimestampFormat, Destination, Styler and StyleName.