@gouranga_samrat/log-wiz
v1.0.2
Published
The ultra-lightweight, high-performance logger for Node.js and Browser with automatic PII masking and zero dependencies.
Maintainers
Readme
🧙 log-wiz
The ultra-lightweight, high-performance logger for Node.js and Browser with automatic PII masking and zero dependencies.
📖 Full Documentation · 🚀 Getting Started · 📦 npm Package · 🐛 Report a Bug
[!TIP] New here? The documentation site covers every feature in detail with interactive examples, a full API reference, and architecture diagrams. This README is a quick-reference summary.
Table of Contents
- Why log-wiz?
- Installation
- Quick Start
- Log Levels
- Automatic PII Masking
- Configuration
- Transports
- Multi-Instance Loggers
- File Rotation
- Error Logging
- Correlation IDs
- Browser Support
- No-Op Mode
- TypeScript
- Custom Transports
- API Reference
- Contributing
- License
Why log-wiz?
| Feature | log-wiz | winston | pino | |---|:---:|:---:|:---:| | Zero runtime dependencies | ✅ | ❌ | ❌ | | Automatic PII masking | ✅ | ❌ | ❌ | | Browser-native (< 1.5 KB gz) | ✅ | ❌ | ⚠️ | | Circular-reference safe | ✅ | ⚠️ | ✅ | | Structured stack-trace parsing | ✅ | ❌ | ⚠️ | | Multi-instance + scoped loggers | ✅ | ✅ | ✅ | | Built-in daily file rotation | ✅ | plugin | ✅ | | No-op mode (zero overhead) | ✅ | ⚠️ | ⚠️ | | Tree-shakable exports | ✅ | ❌ | ❌ |
Installation
npm install @gouranga_samrat/log-wiz
# or
yarn add @gouranga_samrat/log-wiz
# or
pnpm add @gouranga_samrat/log-wizRequirements: Node.js ≥ 18 · TypeScript ≥ 4.7 (optional) · Any modern browser · 0 runtime dependencies
Quick Start
import { wiz } from '@gouranga_samrat/log-wiz';
// Zero config — auto-detects environment
wiz.info('Server started', { meta: { port: 3000 } });
wiz.warn('Rate limit approaching', { meta: { current: 980, limit: 1000 } });
wiz.error('DB connection failed', { error: new Error('ECONNREFUSED') });Development output (pretty, coloured):
█ INF 2024-05-15 14:32:01.123 Server started
meta: { "port": 3000 }
█ ERR 2024-05-15 14:32:02.456 DB connection failed
Error: ECONNREFUSED
at connect (src/db/client.ts:22:9)Production output (compact JSON, auto-selected when NODE_ENV=production):
{"timestamp":"2024-05-15T14:32:01.123Z","level":"info","env":"node","message":"Server started","meta":{"port":3000}}[!NOTE] For full output examples, transport screenshots, and framework integrations (Express, Fastify, Next.js), see the Getting Started guide →
Log Levels
Levels are ordered by severity. Entries below the configured level are silently dropped with zero overhead.
| Level | Severity | Use case |
|-------|:--------:|----------|
| trace | 10 | Step-by-step debugging, very high volume |
| debug | 20 | Development diagnostics |
| info | 30 | Normal operational events (default) |
| warn | 40 | Unexpected but recoverable |
| error | 50 | An operation failed |
| fatal | 60 | Process-level failure, exit imminent |
| none | ∞ | No-op mode — zero output, zero overhead |
const logger = new Wiz({ level: 'warn' }); // drops trace / debug / info[!NOTE] Full level reference with filtering examples → Log Levels →
Automatic PII Masking
The most painful part of logging is accidentally leaking secrets. log-wiz solves this natively — no extra code required.
wiz.info('User login', {
meta: {
username: 'alice',
password: 'hunter2', // → [MASKED] automatically
token: 'eyJhbGci...', // → [MASKED] automatically
authorization: 'Bearer xyz', // → [MASKED] automatically
email: '[email protected]' // visible — not a default masked key
},
});Built-in masked keys: password · token · secret · authorization · cookie · card_number · cvv · ssn · apikey · privatekey — and their common variants (snake_case, camelCase, UPPER_CASE).
Masking is recursive (works on nested objects and arrays), non-mutating (original object untouched), and circular-reference safe (uses a WeakSet — never throws RangeError).
Add custom keys:
const logger = new Wiz({
maskedKeys: ['nationalId', 'medicalRecordNumber'],
});[!NOTE] Deep dive into masking behaviour, key normalisation, and replacing defaults → PII Masking →
Configuration
import { Wiz } from '@gouranga_samrat/log-wiz';
const logger = new Wiz({
level: 'info', // minimum severity to output
scope: 'api', // label shown on every entry
correlationId: 'worker-1', // static ID for this instance
format: 'pretty', // 'pretty' | 'json' | 'browser' (auto-detected)
maskedKeys: ['nationalId'], // extra keys to mask
replaceDefaultMaskedKeys: false, // true = replace defaults, not extend
omitTimestamp: false, // useful for deterministic tests
file: {
dir: './logs', // log directory (created automatically)
maxFiles: 7, // retain this many daily files
asyncBuffer: true, // batch writes — non-blocking
bufferSize: 100, // flush every 100 entries...
flushIntervalMs: 1000, // ...or every 1 second
},
});
// Reconfigure at runtime — no restart needed
logger.setConfig({ level: 'debug' });[!NOTE] Every option documented with types, defaults, and examples → Configuration Reference →
Transports
log-wiz auto-detects your environment and picks the right transport:
| Environment | Auto-selected transport | Output style |
|---|---|---|
| Development (Node.js) | ConsolePrettyTransport | Rich ANSI colours, multi-line |
| Production / CI | ConsoleJsonTransport | Single-line NDJSON |
| Browser | ConsoleBrowserTransport | Grouped DevTools output |
| Node.js (any) | FileTransport | Daily rotating NDJSON files |
Override with format: 'pretty' | 'json' | 'browser'.
[!NOTE] Transport details, custom transport API, and sink examples (Datadog, Splunk, Slack) → Transports →
Multi-Instance Loggers
Create fully independent loggers per subsystem — each with its own level, scope, masked keys, and transports:
import { Wiz } from '@gouranga_samrat/log-wiz';
const dbLogger = new Wiz({ scope: 'database', level: 'debug' });
const httpLogger = new Wiz({ scope: 'http', level: 'info' });
const authLogger = new Wiz({ scope: 'auth', level: 'trace' });
dbLogger.debug('Connection pool ready', { meta: { poolSize: 10 } });
// → █ DBG ... [database] Connection pool ready
httpLogger.info('GET /healthz → 200', { meta: { latencyMs: 2 } });
// → █ INF ... [http] GET /healthz → 200[!NOTE] Scoped loggers, per-module pattern, and correlation across instances → Multi-Instance →
File Rotation
Stream-based daily log rotation using fs.createWriteStream — non-blocking, no missed entries at rollover:
logs/
├── 2024-05-13.log ← auto-pruned after maxFiles exceeded
├── 2024-05-14.log
└── 2024-05-15.log ← active WriteStream (append mode)const logger = new Wiz({
file: { dir: './logs', maxFiles: 7, asyncBuffer: true },
});
// Always flush before exit
process.on('SIGTERM', async () => {
await logger.close();
process.exit(0);
});[!NOTE] Async buffer, retention policy, graceful shutdown, and multi-directory setups → File Rotation →
Error Logging
Pass an Error object and log-wiz automatically parses the stack trace into structured, readable frames:
try {
await db.query('SELECT ...');
} catch (err) {
logger.error('Query failed', {
error: err as Error,
meta: { table: 'users', operation: 'SELECT' },
});
}Output:
█ ERR 2024-05-15 14:32:01.123 Query failed
TypeError: Cannot read properties of null (reading 'rows')
at executeQuery (src/db/client.ts:42:12)
at UserRepository (src/repos/user.ts:18:5)Correlation IDs
Trace a single request across all loggers and services:
// Instance-level — every entry carries this ID
const logger = new Wiz({ correlationId: 'worker-3' });
// Per-call override
logger.info('Processing', { correlationId: req.headers['x-request-id'] });Browser Support
log-wiz works natively in the browser. The browser bundle is < 1.5 KB gzipped — all Node.js file-system code is excluded at the bundler level via the browser export condition.
// React, Vue, Svelte, or vanilla — identical API
import { wiz } from '@gouranga_samrat/log-wiz';
wiz.info('Component mounted', { meta: { component: 'UserProfile' } });[!NOTE] SSR setup, global error handlers, and per-framework examples → Browser Usage →
No-Op Mode
Set level: 'none' to silence all output without removing any code. Every call returns immediately — zero allocations, zero I/O, true zero overhead.
const logger = new Wiz({
level: process.env.NODE_ENV === 'test' ? 'none' : 'info',
});TypeScript
log-wiz is written entirely in TypeScript and ships exhaustive type declarations. No @types/ package needed.
import type { WizConfig, LogEntry, IWiz, LogLevel, Transport } from '@gouranga_samrat/log-wiz';
// Full IntelliSense on config
const config: WizConfig = { level: 'debug', scope: 'payments' };
// Type your dependency injection
class PaymentService {
constructor(private readonly logger: IWiz) {}
}
// Typed custom transport
class MyTransport implements Transport {
write(entry: LogEntry): void { /* ... */ }
}[!NOTE] Full interface reference for every exported type → TypeScript Types →
Custom Transports
Any object implementing the Transport interface works:
import { Wiz } from '@gouranga_samrat/log-wiz';
import type { Transport, LogEntry } from '@gouranga_samrat/log-wiz';
class DatadogTransport implements Transport {
private queue: LogEntry[] = [];
write(entry: LogEntry): void {
this.queue.push(entry);
if (this.queue.length >= 25) this.flush();
}
flush(): void {
if (!this.queue.length) return;
fetch('https://http-intake.logs.datadoghq.com/api/v2/logs', {
method: 'POST',
headers: { 'DD-API-KEY': process.env['DD_API_KEY']! },
body: JSON.stringify(this.queue.splice(0)),
});
}
async close(): Promise<void> { this.flush(); }
}
const logger = new Wiz({ file: false });
(logger as any).transports.push(new DatadogTransport());[!NOTE] Sink examples for Datadog, Splunk, Slack, and in-memory testing → Custom Transports →
API Reference
new Wiz(config?)
Creates a new independent logger instance.
wiz.trace / .debug / .info / .warn / .error / .fatal(message, options?)
| Parameter | Type | Description |
|-----------|------|-------------|
| message | string | Primary human-readable message |
| options.meta | Record<string, unknown> | Structured metadata — deep-cloned and PII-masked |
| options.error | Error | Error object — stack parsed into StackFrame[] |
| options.correlationId | string | Overrides the instance correlationId for this call |
logger.setConfig(partial)
Merges updates into the running instance. Rebuilds transports when format or file changes.
logger.flush()
Synchronously drains all transport write buffers.
async logger.close()
Flushes buffers and releases all resources (file handles, timers). Always await before process.exit().
[!NOTE] Complete API documentation with all parameter types, return values, and examples → API Reference →
Contributing
Contributions, bug reports, and feature requests are welcome.
git clone https://github.com/GourangaDasSamrat/log-wiz.git
cd log-wiz
npm install
npm test # 76 tests across 9 suites
npm run test:coverage # with coverage report
npm run build # compiles ESM + CJS + browser + typesPlease read CONTRIBUTING.md before opening a pull request — it covers the commit convention, coding standards, and the zero-runtime-dependencies rule.
For security vulnerabilities, see SECURITY.md and use private reporting rather than opening a public issue.
Documentation
The full documentation site is built with Astro + Starlight and hosted on GitHub Pages:
https://GourangaDasSamrat.github.io/log-wiz-docs/
| Section | Description | |---------|-------------| | Getting Started | Install and write your first log | | PII Masking | How automatic masking works | | Transports | Built-in and custom transports | | Configuration | Every option documented | | API Reference | Complete method signatures | | TypeScript Types | All exported interfaces | | Architecture | How log-wiz works internally |
License
MIT © 2025 GourangaDasSamrat
Made with ❤️ · Documentation · npm · Issues
