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

logger-logs

v2.0.0

Published

logger-logs is a fast, lightweight, structured logger for Node.js, TypeScript & JavaScript. Zero-dependency logging with log levels, JSON output, file/line numbers, timestamps, child loggers, pluggable transports, and built-in sensitive-data redaction.

Readme

📜 logger-logs

The fast, zero-dependency structured logger for Node.js, TypeScript & JavaScript

Levels · JSON & pretty output · file & line numbers · child loggers · async context · rotating file transport · automatic secret redaction — all with zero runtime dependencies.

npm version downloads minzip size zero deps types included license

Quick Start · Features · API · Examples · FAQ · Compare


Why logger-logs?

console.log doesn't scale — no levels, no structure, no context, no redaction, no way to route output. logger-logs gives you everything a production app needs in a tiny, dependency-free package you can read in one sitting.

// ❌ console.log — flat, unstructured, leaks secrets, no source
console.log('user login', { id: 42, password: 'hunter2' });

// ✅ logger-logs — leveled, structured, redacted, with source + timestamp
import { log } from 'logger-logs';
log.info('user login', { id: 42, password: 'hunter2' });
12:34:56.789  INFO   user login  id=42 password=[REDACTED]  (src/auth.ts:18)

…and the same call in production (NODE_ENV=production) emits machine-readable JSON your log platform ingests natively:

{"level":"info","time":"2026-06-06T12:34:56.789Z","pid":4242,"hostname":"web-1","msg":"user login","id":42,"password":"[REDACTED]","caller":"src/auth.ts:18"}

📦 Installation

npm install logger-logs

Also available via yarn add logger-logs · pnpm add logger-logs · bun add logger-logs — requires Node.js 14+, works in both ESM (import) and CommonJS (require).

🚀 Quick Start

import { log, createLogger } from 'logger-logs';

// 1) Use the ready-made default logger
log.info('server started', { port: 3000 });
log.error('payment failed', new Error('gateway timeout'));

// 2) Or configure your own
const logger = createLogger({
  level: 'debug',
  name: 'api',
  context: { service: 'checkout' },
});

logger.debug('processing order', { orderId: 'o-123' });
const { createLogger } = require('logger-logs');

const logger = createLogger({ level: 'info' });
logger.info('hello from CommonJS');

The package ships dual ESM + CommonJS builds with a correct exports map, so both module systems work out of the box.

✨ Features

| | | | --- | --- | | 🎚️ Log levels | trace · debug · info · warn · error · fatal with a runtime-adjustable threshold | | 🧱 Structured output | pretty (colorized), json (NDJSON), and logfmt formatters | | 📍 Source & time | File name, line number, ISO timestamp, pid & hostname on every entry | | 🌳 Child loggers | Bind service / requestId once, inherit transports & config | | 🧵 Async context | Auto-propagate request & trace IDs via AsyncLocalStorage | | 🔌 Transports | Console (stdout/stderr split) + rotating file, or bring your own | | 🔒 Redaction | Masks passwords, tokens & auth headers — deep & case-insensitive | | 🛡️ Safe serialization | Circular refs, BigInt, functions & Error.cause never crash logging | | ⚡ Performance | Lazy call-site capture, sampling, hooks, timers | | 🟦 TypeScript-first | Written in TS, ships .d.ts — no @types needed | | 🪶 Zero dependencies | Nothing to audit, tiny install footprint |

🎚️ Log Levels

Six severities — trace (10), debug (20), info (30), warn (40), error (50), fatal (60). Anything below the logger's level is dropped before any work happens.

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

logger.info('skipped');         // below threshold → no output
logger.error('emitted');

logger.setLevel('debug');       // change at runtime
logger.isLevelEnabled('trace'); // → false

💡 Set the default level from the environment with LOG_LEVEL=debug.

🧱 Structured Logging & Formats

Pass a message plus any number of context objects. Objects are merged into structured fields; Errors are serialized under err automatically.

logger.info('payment captured',
  { amount: 4200, currency: 'USD' },
  { gateway: 'stripe' },
);

Pick the output format that fits the destination:

| Format | Best for | Looks like | | --- | --- | --- | | pretty | local development | 12:34 INFO msg key=value | | json | Datadog · Loki · ELK · CloudWatch | {"level":"info",...} | | logfmt | Grafana · Splunk | level=info msg="..." key=value |

createLogger({ format: 'json' });
createLogger({ format: 'logfmt' });
createLogger({ format: 'pretty', colors: true });

🧵 Async Context (Request / Trace IDs)

Stop threading a logger through every function. Bind context once at the edge of a request and every log in that async call tree carries it automatically — powered by Node's AsyncLocalStorage.

import { log, runWithContext, bindContext } from 'logger-logs';

app.use((req, _res, next) =>
  runWithContext({ requestId: req.id, traceId: req.headers['x-trace-id'] }, next),
);

// Anywhere downstream — no logger passing required:
function chargeCard() {
  log.info('charging card');       // → automatically includes requestId + traceId
  bindContext({ userId: 'u-42' }); // add more to the current scope
}

Perfect for correlating logs across a request and emitting OpenTelemetry-style trace_id / span_id fields.

🌳 Child Loggers

const logger = createLogger({ name: 'api' });
const reqLog = logger.child({ requestId: 'req-9f2', userId: 'u-42' });

reqLog.info('handling request');          // includes requestId + userId
reqLog.error('db error', new Error('deadlock'));

Children inherit their parent's transports and configuration, and can be nested arbitrarily.

🔌 Transports

Transports decide where logs go. Each can carry its own level and formatter.

import { createLogger, consoleTransport, fileTransport, jsonFormatter } from 'logger-logs';

const logger = createLogger({
  transports: [
    consoleTransport(),
    fileTransport({
      path: 'logs/app.log',
      format: jsonFormatter,
      level: 'info',
      maxSize: 10 * 1024 * 1024, // rotate at 10 MB
      maxFiles: 5,               // keep app.log.1 … app.log.5
    }),
  ],
});

await logger.flush(); // drain before exit; logger.close() also releases handles

A transport is just an object with a write method — ship logs anywhere (HTTP, queue, socket):

const httpTransport = {
  name: 'http',
  write(line, entry) {
    fetch('https://logs.example.com', { method: 'POST', body: line });
  },
};

createLogger({ transports: [httpTransport] });

🔒 Sensitive Data Redaction

Secrets should never reach your logs. Common keys are redacted by default — recursively, case-insensitively, and without mutating your original objects.

logger.info('login', { user: 'alice', password: 'hunter2', token: 'abc123' });
// → user=alice  password=[REDACTED]  token=[REDACTED]

createLogger({ redact: ['email', 'phone'] });               // add your own keys
createLogger({ redact: { keys: ['email'], censor: '***' } }); // custom mask

Default keys include password, token, apiKey, authorization, secret, cookie, ssn, creditCard and more.

🛠️ Real-World Example (Express)

import express from 'express';
import { createLogger, runWithContext, fileTransport, consoleTransport } from 'logger-logs';

const logger = createLogger({
  name: 'api',
  level: process.env.LOG_LEVEL ?? 'info',
  transports: [
    consoleTransport(),
    fileTransport({ path: 'logs/api.log', maxSize: 5_000_000, maxFiles: 10 }),
  ],
});

const app = express();

// Attach a request-scoped logger + correlation id to every request.
app.use((req, res, next) => {
  const requestId = crypto.randomUUID();
  runWithContext({ requestId, method: req.method, path: req.path }, () => {
    const timer = logger.startTimer();
    res.on('finish', () =>
      timer.done('request completed', { status: res.statusCode }),
    );
    next();
  });
});

app.get('/health', (_req, res) => {
  logger.debug('health check');
  res.json({ ok: true });
});

app.listen(3000, () => logger.info('server listening', { port: 3000 }));
12:00:00.001  INFO   [api] server listening  port=3000
12:00:03.114  INFO   [api] request completed  requestId=8f3c… method=GET path=/health status=200 durationMs=12

🪝 Advanced: Hooks, Serializers & Sampling

createLogger({
  // Drop or mutate entries right before they're written.
  hooks: [(entry) => (entry.context.healthcheck ? false : undefined)],

  // Transform values bound under a key (pino-style serializers).
  serializers: { user: (u) => ({ id: u.id }) },

  // Emit only 10% of info/debug logs; warn and above are always emitted.
  sampleRate: 0.1,

  // Skip stack capture on hot paths for max throughput.
  captureCallSite: false,
});

📖 API Reference

| Option | Type | Default | Description | | --- | --- | --- | --- | | level | LevelName | LOG_LEVEL or 'info' | Minimum level to emit | | name | string | – | Logger name added to every entry | | context | object | {} | Base fields merged into every entry | | format | 'pretty' \| 'json' \| 'logfmt' \| Formatter | env-based | Output format | | colors | boolean \| 'auto' | 'auto' | ANSI colors for pretty | | captureCallSite | boolean | true | Capture file/line (disable on hot paths) | | transports | Transport[] | [consoleTransport()] | Where logs go | | redact | string[] \| RedactionOptions | defaults | Keys to mask | | serializers | Record<string, Serializer> | {} | Per-key value transforms | | hooks | BeforeLogHook[] | [] | Pre-write middleware | | sampleRate | number | 1 | Fraction of info/debug to emit |

| Method | Description | | --- | --- | | trace / debug / info / warn / error / fatal(...args) | Log at a level | | log(level, ...args) | Log at an explicit level | | child(bindings, { name? }) | Derive a context-bound logger | | setLevel(level) · isLevelEnabled(level) | Inspect/change threshold | | startTimer(){ done(msg?, context?) } | Measure & log durations | | flush() · close() | Drain / release transports |

log (default instance) · createLogger · Logger · consoleTransport · fileTransport · jsonFormatter · prettyFormatter · logfmtFormatter · runWithContext · bindContext · getContext · serializeError · safeStringify · LEVELS · logger (v1 compat).

🔄 Migrating from v1

The original logger() function still works unchanged — no breaking changes to your existing calls:

import { logger } from 'logger-logs';
logger('still works', { like: 'before' });
// → Path::/abs/file.js,  Line::2,   Message::still works { "like": "before" }

For new code, prefer the structured API (log.info(...) / createLogger(...)). See the CHANGELOG for the full v2 release notes.

📊 Comparison with winston & pino

| | logger-logs | winston | pino | | --- | :---: | :---: | :---: | | Runtime dependencies | 0 | many | few | | Log levels | ✅ | ✅ | ✅ | | JSON output | ✅ | ✅ | ✅ | | Pretty dev output | ✅ built-in | plugin | separate pkg | | File & line numbers | ✅ | ❌ | ❌ | | Child loggers | ✅ | ✅ | ✅ | | Async context | ✅ built-in | ❌ | via plugin | | Secret redaction | ✅ built-in | ❌ | ✅ | | Dual ESM + CJS + types | ✅ | partial | ✅ | | Install footprint | tiny | large | small |

logger-logs aims to be the simplest structured logger that still covers levels, transports, context, and redaction — a focused winston / pino alternative for apps that want power without the weight.

❓ FAQ

🤝 Contributing

Contributions are welcome!

npm install
npm run build      # bundle ESM + CJS + types
npm test           # run the Jest suite
npm run lint       # eslint
npm run typecheck  # tsc --noEmit

Found a bug or have an idea? Open an issue or a pull request.

📩 Support & License

Released under the ISC License © Satyam Kushwaha.


logger-logs — node.js logger · javascript logger · typescript logger · structured logging · json logger · winston alternative · pino alternative · log levels · child logger · log redaction · async context logging · zero-dependency logger · rotating file logger

If this package saved you time, a ⭐ on GitHub means a lot.