MagicLogger
🚀 Universal Color Logging Standard
MagicLogger is a TypeScript logger built on a universal color logging standard that preserves styled text across any language, transport, or platform.
Traditional prod environments suppress / strip styling / pretty print in logs, dropping presumed unnecessary bundling and load.
Using this library generally means you're okay with these assumptions:
- Storage is cheap, some extra kb in many web apps makes little difference (if you don't care about an image being 1.1 vs 1.0 mb this likely applies)
- Some logs sent in production will require human review consistently
- When you analyze logs at a high-level you want to have a visually appealing experience
Installation
npm install magiclogger
# or
yarn add magiclogger
# or
pnpm add magiclogger
Supports both ESM and CommonJS:
import { Logger } from 'magiclogger'; // ESM/TypeScript
const { Logger } = require('magiclogger'); // CommonJS
Quick Start
import { Logger, SyncLogger, createLogger, createSyncLogger } from 'magiclogger';
// AsyncLogger (default) - non-blocking, faster for styled output
const logger = new Logger(); // Uses AsyncLogger by default
logger.info('Application started');
// Logger with automatic console transport (enabled by default)
const logger = new Logger({
useConsole: true, // Console transport is enabled by default (can be disabled with false)
useColors: true, // Enable colored output (default: true)
verbose: false // Show debug messages (default: false)
});
// Or simply use with defaults - console is automatically enabled
const logger = new Logger(); // Console transport created automatically
// SyncLogger - only for audit logs that MUST never be dropped
// Use ONLY when you need absolute guarantee of delivery under extreme load
// AsyncLogger is recommended for 99.9% of use cases
const auditLogger = new SyncLogger({
file: './audit.log', // Blocks until written to disk
forceFlush: true // For regulatory compliance
});
Styling APIs
Choose the styling approach that fits your code style:
import { Logger } from 'magiclogger';
const logger = new Logger();
// 1. Inline angle brackets - simplest, works everywhere
logger.info('<green.bold>✅ Success:</> User <cyan>john@example.com</> authenticated');
// 2. Template literals - clean interpolation
logger.info(logger.fmt`@red.bold{ERROR:} Failed to connect to @yellow{${database}}`);
// 3. Chainable API - programmatic styling
logger.info(logger.s.blue.bold('INFO:') + ' Processing ' + logger.s.cyan(filename));
Key Features
Structured Logging with NDJSON
MagicLogger supports NDJSON (Newline Delimited JSON) format: each log entry is a complete JSON object on its own line.
// Configure NDJSON output
const logger = new Logger({
transports: [
new FileTransport({
filepath: './logs/app.log',
format: 'json' // NDJSON format
})
]
});
// Output (each line is a complete JSON object):
// {"id":"abc123","timestamp":"2024-01-20T10:30:00Z","level":"info","message":"Server started","context":{"port":3000}}
// {"id":"def456","timestamp":"2024-01-20T10:30:01Z","level":"error","message":"Database error","error":{"message":"Connection refused"}}
Structured JSON with Optional Validation
Every log outputs structured JSON following the MAGIC Schema:
// With optional schema validation (lazy-loaded)
const logger = new Logger({
schemas: {
user: z.object({
userId: z.string().uuid(),
email: z.string().email()
})
},
onSchemaViolation: 'warn' // 'warn' | 'error' | 'throw'
});
logger.info('User login', { tag: 'user', userId: '550e8400...', email: 'john@example.com' });
🎯 Tagging & Theming
Auto-style logs based on semantic tags:
const logger = new Logger({
theme: {
tags: {
api: ['cyan', 'bold'],
database: ['yellow'],
critical: ['white', 'bgRed', 'bold']
}
}
});
logger.info('Request received', { tags: ['api'] }); // Auto-styled
Transport-Optimized Architecture
MagicLogger uses transport-specific optimization for maximum performance:
Each Transport Manages Its Own Strategy
Transports use the optimal I/O pattern for their use case:
const logger = new Logger({
transports: [
// Console: synchronous for immediate feedback
new ConsoleTransport(),
// File: sonic-boom for non-blocking high-performance I/O
new FileTransport({
filepath: './app.log',
minLength: 4096, // Buffer before auto-flush
maxWrite: 16384 // Max bytes per write
}),
// HTTP: batching with async requests
new HTTPTransport({
endpoint: 'https://logs.example.com',
batch: { size: 100, timeout: 5000 } // Batch 100 logs or flush every 5s
})
]
});
Dispatch Architecture
MagicLogger uses an immediate dispatch architecture:
Logger Level: Immediate dispatch to transports
- AsyncLogger sends logs directly to transports without batching
- Minimal overhead with timestamp caching optimization
- Near-zero latency for log delivery
Transport Level: Optimized batching per transport type
// Each transport handles its own batching strategy
const httpTransport = new HTTPTransport({
batch: { size: 100, timeout: 5000 } // Batch 100 logs or flush every 5s
});
Note: Worker threads are optional. Enable with worker.enabled: true
for CPU-intensive workloads. Style processing in the main thread adds minimal overhead (~0.01ms per log).
Log Delivery Guarantees
Logger (AsyncLogger - default, recommended):
- Non-blocking - keeps your app responsive under load
- Faster for styled output - 120K ops/sec vs 50K for sync
- With graceful shutdown (
await logger.close()
): All transports are flushed - Without graceful shutdown (crash/SIGKILL): Transport buffers may not flush
- Perfect for 99.9% of use cases including production applications
- The default choice for modern applications
SyncLogger (rarely needed):
- Blocks until each log is written to disk (using
fs.appendFileSync
) - Always guarantees delivery - logs are never lost unless OS crashes
- Trade-off: Blocks event loop, can make app unresponsive under load
- Use ONLY for critical audit logs with regulatory requirements
For critical logs that must never be lost:
// Option 1: Use SyncLogger for audit/security logs
const auditLogger = new SyncLogger({ file: './audit.log' });
// Option 2: Ensure graceful shutdown for AsyncLogger
process.on('SIGTERM', async () => {
await logger.close(); // Flushes all pending logs
process.exit(0);
});
// Option 3: Use synchronous transports with AsyncLogger
const logger = new AsyncLogger({
transports: [new SyncFileTransport({ filepath: './critical.log' })]
});
⚡ Performance
Real-World Benchmarks
MagicLogger achieves excellent performance with AsyncLogger as default:
- 164K ops/sec - Plain text (non-blocking)
- 120K ops/sec - Styled output (faster than sync's 50K)
- 0.004ms P50 blocking - Event loop stays responsive
- Non-blocking architecture - Your app stays fast under load
- Smart batching - Automatic optimization for network transports
See our performance documentation for detailed benchmarks and optimization strategies.
MAGIC Schema - Universal Styled Logging Standard
The MAGIC Schema is a universal JSON format that preserves text styling across any language, transport, or platform. Any language can produce MAGIC-compliant logs that MagicLogger (or any MAGIC-compatible system) can ingest and display with full color preservation.
The MAGIC Schema provides:
- Style Preservation: Colors and formatting survive serialization as structured data
- Language Agnostic: Any language can implement the MAGIC producer specification
- Ingestion Ready: MagicLogger can consume logs from any MAGIC-compliant source
- Consistent Structure: Same JSON format across all languages and transports
- OpenTelemetry Ready: Direct compatibility with OTLP (OpenTelemetry Protocol)
- Distributed Tracing: Built-in W3C Trace Context support
- Rich Metadata: Automatic capture of system, process, and environment info
Next Steps
- Full API Documentation - Complete TypeScript API reference
- API Reference - Core APIs and usage patterns
- Advanced Usage - Advanced patterns and techniques
- Architecture Overview - How MagicLogger works internally
- Transports - All available transports and configuration
- Styling Guide - Complete styling and theming options