Beautiful, structured logging that just works. MagicLogger transforms boring console logs into vibrant, organized output while maintaining perfect performance and tree-shaking.
import { Logger } from 'magiclogger';
const logger = new Logger({ useColors: true });
// Simple, beautiful logging
logger.info('Server started', { port: 3000 });
logger.success('User authenticated', { userId: 'user_123' });
logger.warn('Rate limit exceeded', { ip: '192.168.1.1' });
logger.error('Database connection failed', new Error('Timeout'));
// Rich styling with multiple APIs - pick your favorite!
logger.info(logger.s.red.bold('CRITICAL:') + ' Server is ' + logger.s.yellow('shutting down'));
logger.info(logger.fmt`@red.bold{CRITICAL:} Server is @yellow{shutting down} at @cyan{${new Date().toISOString()}}`);
logger.info('<red.bold>CRITICAL:</> Server is <yellow>shutting down</> at <cyan>' + new Date().toISOString() + '</>');
logger.header('🚀 DEPLOYMENT STARTED');
logger.progressBar(75);
logger.table([
{ service: 'API', status: 'healthy', uptime: '99.9%' },
{ service: 'DB', status: 'degraded', uptime: '95.2%' }
]);MagicLogger is designed to be fast, even with rich styling features. The benchmarks compare MagicLogger against popular logging libraries in various scenarios.
# Install dependencies (optional loggers for comparison)
npm install
npm run install-deps
# Run benchmarks (view-only, no updates)
npm run perf:run # Run benchmarks and display results
# ⚠️ MANUAL UPDATE ONLY - DO NOT RUN IN CI/CD
# Updates README and benchmark-results.md with new performance data
# Only run this when you've made performance improvements and want to update docs
npm run perf:update # Updates benchmark files (run manually when needed)Important: The perf:update command is NOT run automatically during builds, deploys, or CI/CD.
It should only be run manually when you've made performance improvements and want to update the documentation.
- MagicLogger Sync vs Winston and Pino (in sync mode)
- Tests blocking I/O operations with immediate writes
- Measures: Object creation, routing, and dispatch overhead
- MagicLogger Async vs Pino Async
- Tests non-blocking operations with buffered writes
- Both use
setImmediatecallbacks for truly async behavior - Measures: Buffering efficiency and throughput
- With JSON serialization: Pino ~245k ops/sec, MagicLogger ~94k ops/sec
- Pino is 2.6x faster in production scenarios
- Both are "fast enough" for 99% of applications
// Async stream for fair comparison
class AsyncBatchStream extends Writable {
_write(chunk, encoding, callback) {
this.count++;
setImmediate(callback); // Truly async
}
}
// Setup both sync and async Pino loggers
pinoLogger = pino({ sync: true }, syncNullStream);
pinoAsyncLogger = pino({ sync: false }, asyncBatchStream);- Tests MagicLogger with TypeScript imports
- Includes Pino async comparison
- Measures both synthetic and realistic performance
Our benchmarks measure object creation overhead, NOT the full logging pipeline:
// What we measure (fast)
logger.info('message', { data }); // → NullTransport
// What production uses (slower)
logger.info('message', { data }); // → JSON → File/Network I/OPino's async mode buffers internally and doesn't process all logs immediately:
// Test showing Pino's buffering behavior
for (let i = 0; i < 10000; i++) {
pinoAsync.info({ i }, 'test');
}
// Only ~1400 logs processed immediately
// Rest are buffered and flushed laterAt typical application rates (1-10k logs/sec):
- Difference is negligible (0.006ms vs 0.01ms per log)
- I/O becomes the bottleneck, not the logger
- Both are more than sufficient
| Logger | Type | Ops/sec | Avg (ms) | P95 | P99 |
|---|---|---|---|---|---|
| Plain Text Performance | |||||
| Pino | Sync | 446,051 | 0.002 | 0.004 | 0.010 |
| Winston | Sync | 267,546 | 0.003 | 0.005 | 0.033 |
| MagicLogger | Async | 164,012 | 0.006 | 0.005 | 0.027 |
| MagicLogger | Sync | 147,547 | 0.006 | 0.003 | 0.009 |
| Styled Output Performance | |||||
| Winston | Sync | 211,457 | 0.005 | 0.018 | 0.042 |
| Pino (Manual ANSI) | Async | 155,954 | 0.006 | 0.006 | 0.030 |
| MagicLogger | Async | 120,628 | 0.008 | 0.061 | 0.178 |
| Pino (Pretty) | Async | 102,559 | 0.009 | 0.011 | 0.113 |
| MagicLogger | Sync | 50,195 | 0.020 | 0.025 | 0.078 |
- Async is 3.4x faster for plain text (258k vs 76k ops/sec)
- Worker threads provide true parallelism but add IPC overhead
- Styled messages suffer from IPC serialization (28k ops/sec)
- Thread isolation: Transport crashes don't affect main thread
- True async: Non-blocking I/O operations
- CPU parallelism: Serialization happens off main thread
- Trade-off: IPC overhead for styled messages
- Large batch sizes (5000) reduce IPC calls
- 4 worker threads for better parallelism
- Fast path for unstyled messages
- Aggressive style caching
Generated via scripts/performance/perf-bench.mjs
| Comparison | Result |
|---|---|
| MagicLogger vs Winston | 5-7x faster |
| MagicLogger vs Pino Sync | 1.5-1.9x faster |
| Comparison | Result |
|---|---|
| MagicLogger vs Pino Async | Varies by run |
| Note | Both use different buffering strategies |
- Simpler architecture: Less abstraction layers
- Fast path optimization: 90% of logs skip styling
- Static metadata caching: Reduces repeated work
- Optimized JSON serialization: fast-json-stringify
- Smaller log entries: ~80 bytes vs ~180 bytes
- Native modules: C++ bindings for hot paths
- Mature async buffering: Years of optimization
✅ Sync operations: >50k ops/sec
✅ Async operations: >100k ops/sec
✅ Consistent results across runs
✅ Fair comparison (same workload)
❌ Unrealistically high numbers (>1M ops/sec with I/O) ❌ Async slower than sync (likely misconfigured) ❌ Large variance between runs (>20%)
Our benchmarks show:
- MagicLogger excels at synchronous logging (faster than Winston and competitive with Pino)
- Pino leads in production scenarios (2.6x faster with full serialization)
- Both are fast enough for 99% of applications
- Choose based on needs: Developer experience (MagicLogger) vs raw speed (Pino)
- Test Environment: Node.js 18+ with V8 optimizations
- Iterations: 20,000 operations per test
- Warmup: 1,000 operations before measurement
- Output Suppression: All I/O redirected to null streams to measure pure logging performance
- Test Data: Realistic log messages with metadata objects
- Garbage Collection: Forced between tests when available
MagicLogger delivers competitive performance while providing features other loggers don't:
- Rich Styling: Colors, formatting, and visual elements with minimal overhead
- Multiple APIs: Chain syntax, template literals, and inline markup
- Structured Output: Consistent JSON format for all transports
- Tree Shaking: Only pay for features you use
- Minimal Dependencies: Only sonic-boom for high-performance file I/O
npm install magiclogger
# or
yarn add magiclogger
# or
pnpm add magicloggerimport { Logger } from 'magiclogger';
const logger = new Logger({ useColors: true });
// Standard logging levels
logger.info('Application started successfully');
logger.success('User registration completed');
logger.warn('Disk space running low (15% remaining)');
logger.error('Failed to connect to database');
logger.debug('Auth token: eyJhbGciOiJIUzI1NiIs...');
// Universal log method
logger.log('Message with default info level');
logger.log('Warning message', 'warn');
logger.log('Error message', 'error');MagicLogger emits a consistent JSON structure to transports. Below are real inputs and the resulting JSON objects your transports receive.
import { Logger } from 'magiclogger';
const logger = new Logger({ id: 'api', tags: ['service', 'api'] });
// 1) Plain metadata object
logger.info('User login', { userId: 'u_123', ip: '203.0.113.10' });
// 2) Direct Error instance
logger.error('Payment failed', new Error('Card declined'));
// 3) Metadata object containing an error
logger.error('DB query failed', {
error: new Error('timeout'),
query: 'SELECT * FROM users WHERE id = ?'
});Example JSON produced:
{
"id": "1733938475123-abc123xyz",
"timestamp": "2025-08-14T12:34:35.123Z",
"timestampMs": 1765769675123,
"level": "info",
"message": "User login",
"plainMessage": "User login",
"loggerId": "api",
"tags": ["service", "api"],
"context": { "userId": "u_123", "ip": "203.0.113.10" },
"metadata": {
"hostname": "my-host",
"pid": 1234,
"platform": "linux",
"nodeVersion": "v18.20.8"
}
}MagicLogger provides three complementary styling APIs. Use one or combine them seamlessly!
const error = logger.s.red.bold('ERROR:');
const warning = logger.s.yellow.underline('Warning:');
const success = logger.s.green.bold('✓ Success');
logger.info(error + ' Connection failed');
logger.info(warning + ' High memory usage');
logger.info(success + ' Deployment complete');logger.info(logger.fmt`@red.bold{Error:} Failed to connect to @yellow.underline{users_db}`);
const errorType = 'Connection Error';
logger.error(logger.fmt`@red.bold{${errorType}} Database failed`);logger.info('<green.bold>SUCCESS:</> All tests passed');
logger.error('<red>Error:</> Connection to <yellow>database</> failed');
logger.warn('<yellow.bold>⚠ Warning:</> <cyan>CPU usage</> is high');const logger = new Logger({
theme: 'ocean' // or 'forest', 'sunset', 'minimal', 'cyberpunk'
});const logger = new Logger({
theme: {
tags: {
api: ['cyan', 'bold'],
database: ['yellow'],
error: ['red', 'bold']
}
},
tags: ['api']
});
// Tags automatically apply their theme styles
logger.info('Request received', { tags: ['api'] });import { Logger } from 'magiclogger';
import {
ConsoleTransport,
FileTransport,
HTTPTransport,
S3Transport,
MongoDBTransport,
} from 'magiclogger/transports';
const logger = new Logger({
transports: [
new ConsoleTransport({ level: 'debug', useColors: true }),
new FileTransport({ filepath: './logs/app.log', maxFiles: 7 }),
new HTTPTransport({ url: 'https://logs.example.com', batch: true }),
new S3Transport({ bucket: 'my-logs', region: 'us-east-1' }),
new MongoDBTransport({ uri: 'mongodb://localhost:27017' }),
]
});logger.header('🚀 DEPLOYMENT PROCESS');
logger.progressBar(75);
logger.table([
{ name: 'API', status: 'healthy', cpu: '12%' },
{ name: 'Database', status: 'healthy', cpu: '45%' },
{ name: 'Cache', status: 'degraded', cpu: '78%' }
]);| Scenario | Size (gzip) |
|---|---|
| core (esm) | 37 kB |
| core + console | 37 kB |
| core + all transports | 45.4 kB |
| all compatibility layers | 43.8 kB |
MagicLogger's performance tests validate high-volume styled logging within strict thresholds. External logger comparisons are performed without styling and with output suppressed for fair comparison.
- Zero-overhead sync logging by default
- Optional async logging with ring buffers for high throughput
- Efficient styling that doesn't impact core performance
- Smart output suppression during benchmarks
- Tree-shaking support - only pay for what you use
All benchmarks are run with:
- Output completely suppressed (null streams/transports)
- Multiple warmup iterations
- Garbage collection between tests
- Realistic log data with metadata
- Multiple test scenarios (sync/async, styled/unstyled)
The results show MagicLogger delivers competitive performance while providing rich formatting features that other loggers lack.