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

@clevercloud/scribe

v0.0.2

Published

Structured logging library built on Pino with stdout/stderr splitting, file rotation, and namespaced loggers

Readme

@clevercloud/scribe

Structured logging library built on Pino with stdout/stderr splitting, file rotation, and namespaced loggers.

Install

pnpm add @clevercloud/scribe

Quick start

import { setup, getLogger, shutdown } from '@clevercloud/scribe';

setup({ stdio: { level: 'info' } });

const log = getLogger('my-service');
log.info('Server started');
log.error({ err }, 'Something went wrong');

// Flush before exit
await shutdown();

API

setup(config)

Initializes the logging system. Must be called once before getLogger().

setup({
  stdio: { level: 'info', pretty: true },
});

| Option | Type | Default | Description | | ----------------- | ----------------------- | ------- | ---------------------------------------- | | namespaceLevels | Record<string, Level> | — | Per-namespace level overrides | | resolveModule | boolean | false | Attach caller file path to each logger | | stdio | StdioTransportConfig | — | Stdio transport config | | file | FileTransportConfig | — | File transport config | | redact | string[] | — | Dot-notation paths to redact from output |

When neither stdio nor file is provided, the logger is fully silent.

Throws if called twice without calling shutdown() first.

StdioTransportConfig

| Option | Type | Default | Description | | -------- | --------- | ------- | ------------------------------------------------ | | level | Level | — | Minimum severity level for stdout/stderr | | pretty | boolean | false | Enable human-readable formatting via pino-pretty |

FileTransportConfig

| Option | Type | Default | Description | | ---------- | ------------------------------- | ------- | ------------------------------------------------------------------------------------------ | | level | Level | — | Minimum severity level for file output | | path | string | — | File path to write logs to (required) | | rotation | boolean \| FileRotationConfig | false | true enables rotation with defaults; an object customizes it; false disables rotation. |

getLogger(namespace?)

Returns a Pino Logger. The namespace argument is optional.

const log = getLogger('http');
log.info('Listening on port 8080');
// → {"level":30,"severity":"info","namespace":"http","module":"/src/server.ts","msg":"Listening on port 8080",...}

const rootLog = getLogger();
rootLog.info('App started');
// → {"level":30,"severity":"info","module":"/src/server.ts","msg":"App started",...}

Each logger includes:

  • namespace — the name passed to getLogger(), omitted when called without an argument
  • module — the caller's file path (when resolveModule is enabled)
  • All fields injected by registered mixins

If a per-namespace level is configured via namespaceLevels, it controls what the logger emits. When multiple transports are configured, each transport still applies its own level filter independently. A logger created without a namespace cannot be targeted by namespaceLevels and inherits the root level.

addMixin(name, fn)

Registers a function that injects dynamic fields into every log entry.

import { addMixin } from '@clevercloud/scribe';

addMixin('requestId', () => asyncLocalStorage.getStore()?.requestId);
  • Return null or undefined to omit the field from a given entry.
  • Re-registering with the same name replaces the previous function.
  • If the function throws, the error propagates through the log.*() call.

shutdown()

Flushes all buffered log entries and resets internal state. Safe to call multiple times or before setup().

process.on('SIGTERM', async () => {
  log.info('Shutting down…');
  await shutdown();
  process.exit(0);
});

Transport

Scribe routes logs based on severity:

| Destination | Levels | | ----------- | ------------------------ | | stdout | trace, debug, info, warn | | stderr | error, fatal |

When file is configured, all levels are additionally written to file.

Per-transport log levels

Each transport specifies its own log level:

setup({
  stdio: { level: 'warn' },
  file: {
    level: 'debug',
    path: '/var/log/app.log',
  },
});

In this example, only warn and above appear in the console, while debug and above are written to file.

File rotation

File rotation is powered by pino-roll and controlled by the rotation field of the file transport:

  • false (or omitted) — rotation is disabled; logs are written to a plain file.
  • true — rotation is enabled with all default settings.
  • A FileRotationConfig object — rotation is enabled with the provided overrides.
// Enable rotation with all defaults
setup({
  file: {
    level: 'info',
    path: '/var/log/app.log',
    rotation: true,
  },
});

// Customize rotation
setup({
  file: {
    level: 'info',
    path: '/var/log/app.log',
    rotation: {
      size: '50m',
      frequency: 'daily',
      maxFiles: 14,
    },
  },
});

| Option | Type | Default | Description | | ------------ | ------------------------------- | -------------- | -------------------------------------------------------- | | size | string \| number | '50m' | Max file size ('1k', '50m', '1g', or number in MB) | | frequency | 'daily' \| 'hourly' \| number | 'daily' | Rotation frequency (or milliseconds) | | dateFormat | string | 'yyyy-MM-dd' | date-fns format for rotated file suffix | | maxFiles | number | 7 | Maximum rotated files to keep | | symlink | boolean | false | Create a current.log symlink |

Per-namespace log levels

Control what individual loggers emit, regardless of the transport level:

setup({
  stdio: { level: 'info' },
  namespaceLevels: {
    database: 'debug',
    http: 'warn',
  },
});

getLogger('database').debug('Query executed'); // visible
getLogger('http').info('Request received'); // hidden (below warn)

Namespace levels set the logger's emission threshold. When multiple transports are configured, each transport still applies its own level filter independently.

Single transport — namespace level controls what appears:

setup({
  stdio: { level: 'warn' },
  namespaceLevels: { verbose: 'debug' },
});

const log = getLogger('verbose');
log.debug('…'); // ✓ appears on stdout (namespace level wins)
log.info('…'); // ✓ appears on stdout

Multiple transports — each transport filters independently:

setup({
  stdio: { level: 'warn' },
  file: { level: 'debug', path: '/var/log/app.log' },
  namespaceLevels: { verbose: 'debug' },
});

const log = getLogger('verbose');
log.debug('…'); // ✗ not on stdout (filtered by stdio level)  ✓ in file
log.info('…'); //  ✗ not on stdout (filtered by stdio level)  ✓ in file
log.warn('…'); //  ✓ on stdout                                ✓ in file

Redaction

Sensitive fields can be redacted from log output using Pino's built-in redaction:

setup({
  stdio: { level: 'info' },
  redact: ['password', 'req.headers.authorization'],
});

const log = getLogger('auth');
log.info({ password: 's3cret', user: 'alice' }, 'login attempt');
// → {"password":"[Redacted]","user":"alice","msg":"login attempt",...}

Paths use dot notation for nested properties. See the Pino redaction docs for wildcard and advanced path syntax.

License

See LICENSE.