🌐 Documentation & Website • 📚 API Reference
MagicLogger is a TypeScript logger that works in Node and browser 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:
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
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
});
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));
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"}}
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' });
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
AsyncLogger (default) - Non-blocking, 120K+ ops/sec with styles. Use for 99.9% of cases.
SyncLogger - Blocks until written to disk. Only for regulatory/audit requirements.
// For critical logs that must never be lost
const auditLogger = new SyncLogger({ file: './audit.log' });
// Or ensure graceful shutdown
process.on('SIGTERM', async () => {
await logger.close(); // Flushes all pending logs
process.exit(0);
});
Note: See Performance Design for architecture details and Advanced Usage for production configurations.
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.
Every log entry outputs this JSON structure:
logger.info('User authenticated', {
userId: 'u_123',
method: 'OAuth',
provider: 'google'
});
Produces:
{
"id": "1733938475123-abc123xyz",
"timestamp": "2025-08-14T12:34:35.123Z",
"timestampMs": 1765769675123,
"level": "info",
"message": "User authenticated", // Plain text message
"styles": [[0, 4, "green.bold"]], // Preserved styling info
"service": "auth-api",
"environment": "production",
"loggerId": "api-service",
"context": {
"userId": "u_123",
"method": "OAuth",
"provider": "google"
},
"metadata": {
"hostname": "api-server-01",
"pid": 12345,
"platform": "linux",
"nodeVersion": "v20.10.0"
},
"trace": {
"traceId": "0af7651916cd43dd8448eb211c80319c",
"spanId": "b7ad6b7169203331"
}
}
The MAGIC Schema maps directly to OpenTelemetry's log data model:
// MAGIC fields → OTLP mapping
{
"id" → attributes["log.id"]
"timestamp" → timeUnixNano
"level" → severityNumber
"message" → body
"trace.traceId" → traceId (root level)
"trace.spanId" → spanId (root level)
"service" → resource.attributes["service.name"]
}
Full Documentation: See the MAGIC Schema Specification for complete field definitions, examples, and integration guides.
The MAGIC Schema is an open specification that any language can implement:
{
"id": "unique-identifier",
"timestamp": "2024-01-15T10:30:00.000Z",
"timestampMs": 1705316400000,
"level": "info|warn|error|debug|trace|fatal",
"message": "Plain text without formatting",
"styles": [
[0, 6, "red.bold"], // Apply red.bold to characters 0-6
[12, 28, "cyan"] // Apply cyan to characters 12-28
]
}
import { Logger } from 'magiclogger';
const logger = new Logger();
// Input with styles
logger.error('<red.bold>Error:</> Database <yellow>timeout</>');
// Outputs MAGIC JSON
{
"message": "Error: Database timeout",
"styles": [[0, 6, "red.bold"], [16, 23, "yellow"]],
// ... other fields
}
// Can reconstruct styled output
import { applyStyles } from 'magiclogger';
const styled = applyStyles(entry.message, entry.styles);
console.log(styled); // Shows with colors!
For a logger to be MAGIC-compliant, it must:
Output Valid MAGIC Schema JSON
{
"message": "Error: Database connection failed",
"styles": [[0, 6, "red.bold"], [7, 35, "yellow"]],
"level": "error",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Preserve Style Information
<red>text</>
)[startIndex, endIndex, style]
tuplesmessage
fieldInclude Required Fields
id
, timestamp
, level
, message
service
, environment
, trace
import {
ConsoleTransport,
FileTransport, // High-performance sonic-boom (default)
WorkerFileTransport, // Worker thread isolation
SyncFileTransport, // Synchronous with buffering
HTTPTransport,
WebSocketTransport
} from 'magiclogger/transports';
// Logger automatically uses high-performance file transport
const logger = new Logger({
file: './logs/app.log' // Uses FileTransport (sonic-boom) automatically
});
// Or explicitly configure transports
const logger = new Logger({
transports: [
// Console with colors (optional - added by default if no transports specified)
new ConsoleTransport({ useColors: true }),
// FileTransport - recommended default (sonic-boom)
new FileTransport({
filepath: './logs/app.log',
minLength: 4096, // Buffer size before auto-flush
maxWrite: 16384 // Max bytes per write
}),
// HTTP with batching
new HTTPTransport({
url: 'https://logs.example.com',
batch: { size: 100, timeout: 5000 }
})
]
});
These are optional dependencies.
// Database transports
import { PostgreSQLTransport, MongoDBTransport } from 'magiclogger/transports';
// Cloud storage
import { S3Transport } from 'magiclogger/transports';
// Messaging systems
import { KafkaTransport, SyslogTransport } from 'magiclogger/transports';
// Observability platforms
import { OTLPTransport } from 'magiclogger/transports/otlp';
const otlpTransport = new OTLPTransport({
endpoint: 'http://localhost:4318',
serviceName: 'my-service',
includeTraceContext: true // W3C Trace Context support
});
By default, MagicLogger automatically creates a console transport unless explicitly disabled:
// Default behavior - console transport is automatically created
const logger = new Logger(); // Console output enabled
// Explicitly disable console for file-only logging (better performance)
const fileOnlyLogger = new Logger({
useConsole: false, // Disable automatic console transport
transports: [
new FileTransport({
filepath: './app.log',
buffer: { size: 1000 } // Buffer for better write performance
})
]
});
// Production setup - disable console
const prodLogger = new Logger({
useConsole: false, // No console overhead in production
transports: [
new HTTPTransport({
url: process.env.LOG_ENDPOINT,
batch: { size: 1000, timeout: 10000 }
}),
new S3Transport({
bucket: 'logs',
compress: true
})
]
});
// Headers and separators
logger.header('🚀 DEPLOYMENT PROCESS');
logger.separator('=', 50);
// Progress bars
for (let i = 0; i <= 100; i += 10) {
logger.progressBar(i);
await delay(100);
}
// Tables
logger.table([
{ name: 'API', status: 'healthy', cpu: '12%' },
{ name: 'Database', status: 'healthy', cpu: '45%' }
]);
// Object diffs
logger.diff('State change', oldState, newState);
MagicLogger's theme system provides consistent, semantic styling across your application.
const logger = new Logger({ theme: 'ocean' });
// Available themes: ocean, forest, sunset, minimal, cyberpunk, dark, default
// Each theme provides consistent colors for semantic log types
logger.info('Information'); // Themed as info style
logger.success('Completed'); // Themed as success style
logger.warning('Caution'); // Themed as warning style
logger.error('Failed'); // Themed as error style
const logger = new Logger({
theme: {
// Log level styles
info: ['cyan'],
success: ['green', 'bold'],
warning: ['yellow'],
error: ['red', 'bold'],
debug: ['gray', 'dim'],
// UI element styles
header: ['brightWhite', 'bold', 'underline'],
footer: ['gray', 'dim'],
separator: ['blue'],
highlight: ['brightYellow', 'bold'],
muted: ['gray', 'dim'],
// Custom semantic styles
api: ['cyan', 'bold'],
database: ['yellow'],
cache: ['magenta'],
network: ['blue'],
security: ['red', 'bold', 'underline']
}
});
Combine themes with tags for automatic styling based on log categories:
const logger = new Logger({
theme: {
tags: {
'api': ['cyan', 'bold'],
'api.request': ['cyan'],
'api.response': ['brightCyan'],
'database': ['yellow'],
'database.query': ['yellow', 'dim'],
'database.error': ['red', 'bold'],
'security': ['red', 'bold', 'bgYellow'],
'performance': ['magenta', 'bold']
}
}
});
// Tags automatically apply themed styles
logger.info('Request received', { tags: ['api', 'api.request'] });
logger.error('Query timeout', { tags: ['database', 'database.error'] });
logger.warn('Unauthorized access attempt', { tags: ['security'] });
MagicLogger supports custom color registration for brand-specific palettes and advanced terminal features.
// Register individual custom color
logger.registerCustomColor('brandPrimary', {
hex: '#FF5733', // 24-bit RGB color (for modern terminals)
fallback: 'orange' // Required fallback for limited terminals
});
// Register multiple custom colors
logger.registerCustomColors({
// Using RGB values
brandBlue: {
rgb: [51, 102, 255],
fallback: 'blue',
description: 'Primary brand blue'
},
// Using 256-color palette
darkOlive: {
code256: 58, // 256-color palette code
fallback: 'green',
description: 'Secondary accent color'
},
// Direct ANSI sequence (advanced)
brandGradient: {
ansi: '\x1b[38;2;255;87;51m', // Direct ANSI escape
fallback: 'red'
}
});
// In themes
logger.setTheme({
header: ['brandPrimary', 'bold'],
success: ['brandBlue'],
accent: ['darkOlive', 'italic']
});
// With style factories
const brand = logger.color('brandPrimary', 'bold');
const accent = logger.color('darkOlive');
logger.info(`Welcome to ${brand('Our Product')} - ${accent('v2.0')}`);
// In styled messages
logger.info('<brandPrimary.bold>Important:</> Check the <brandBlue>dashboard</>');
MagicLogger provides powerful context and tagging features for structured logging, enabling better log organization, filtering, and analysis.
Context allows you to attach structured data to log entries, providing rich metadata for debugging and monitoring.
// Global context - applied to all logs from this logger
const logger = new Logger({
id: 'payment-service',
context: {
service: 'payment-api',
version: '2.1.0',
environment: process.env.NODE_ENV,
region: 'us-east-1',
instanceId: process.env.INSTANCE_ID
}
});
// Per-log context - specific to individual log entries
logger.info('Payment processed', {
orderId: 'ORD-12345',
customerId: 'CUST-67890',
amount: 99.99,
currency: 'USD',
processingTime: 145,
paymentMethod: 'credit_card'
});
// Context merging - per-log overrides global
logger.info('Special payment', {
amount: 199.99,
version: '2.2.0', // Overrides global version
promotional: true // Adds new field
});
When using console-like variadic arguments, wrap context to prevent it from being printed:
import { meta } from 'magiclogger';
// Without meta() - context gets printed to console
logger.info('User logged in', { userId: '123' });
// Output: User logged in { userId: '123' }
// With meta() - context is attached but not printed
logger.info('User logged in', meta({ userId: '123' }));
// Output: User logged in
// Context still available in structured output/transports
import { ContextManager } from 'magiclogger';
const contextManager = new ContextManager({
// Auto-redact sensitive fields
sensitiveKeys: ['password', 'token', 'ssn', 'creditCard'],
// Transform nested keys to flat structure
transformRules: {
'user.id': 'userId',
'request.id': 'requestId',
'response.time': 'responseTime'
},
// Validation rules
maxDepth: 3,
maxSize: 1000, // bytes
forbidden: ['__proto__', 'constructor']
});
// Sanitize sensitive data automatically
const userContext = {
userId: '123',
email: 'user@example.com',
password: 'secret123', // Will be redacted
creditCard: '4111111111111111' // Will be redacted
};
const sanitized = contextManager.sanitize(userContext);
// Result: {
// userId: '123',
// email: 'user@example.com',
// password: '***',
// creditCard: '***'
// }
Tags are simple string labels for categorizing and filtering logs, enabling powerful log organization and styling.
// Global tags - applied to all logs
const logger = new Logger({
tags: ['api', 'production', 'v2']
});
// All logs include these tags
logger.info('Server started'); // Tags: ['api', 'production', 'v2']
logger.error('Database error'); // Tags: ['api', 'production', 'v2']
// Per-log tags - additional categorization
logger.info('User authenticated', {
tags: ['auth', 'oauth', 'google']
});
// Combined tags: ['api', 'production', 'v2', 'auth', 'oauth', 'google']
MagicLogger supports hierarchical tag organization using both dot notation and explicit parent-child relationships:
const logger = new Logger({
tags: ['api.v2']
});
// Dot notation - automatic hierarchy
logger.info('Database query', {
tags: ['database.query.select', 'performance.slow']
});
// Results in tags that can be filtered at any level:
// - 'api.v2' (matches: api, api.v2)
// - 'database.query.select' (matches: database, database.query, database.query.select)
// - 'performance.slow' (matches: performance, performance.slow)
// Explicit parent-child relationships
logger.info('User action', {
tags: [
{ name: 'user', children: ['auth', 'profile'] },
{ name: 'api', children: ['request', 'response'] }
]
});
// Generates: ['user', 'user.auth', 'user.profile', 'api', 'api.request', 'api.response']
// Path-based tag generation
import { TagManager } from 'magiclogger';
const tagManager = new TagManager();
// Generate from file paths
const tags = tagManager.fromPath('src/services/payment/stripe.ts');
// Result: ['src', 'src.services', 'src.services.payment', 'src.services.payment.stripe']
// Generate from class/method names
const methodTags = tagManager.fromMethod('PaymentService', 'processRefund');
// Result: ['PaymentService', 'PaymentService.processRefund']
Filter logs at transport level based on tag hierarchy:
const logger = new Logger({
transports: [
{
type: 'file',
path: './app.log',
filter: (entry) => {
// Include all API-related logs
return entry.tags?.some(tag =>
tag.startsWith('api.') || tag === 'api'
);
}
},
{
type: 'file',
path: './errors.log',
filter: (entry) => {
// Only error and security tags
return entry.tags?.some(tag =>
tag.includes('error') || tag.startsWith('security.')
);
}
}
]
});
Apply styles based on tag hierarchy with cascading rules:
const logger = new Logger({
theme: {
tags: {
// Base styles
'api': ['cyan'],
'database': ['yellow'],
'security': ['red', 'bold'],
// More specific styles override base
'api.error': ['red', 'bold'],
'api.success': ['green'],
'database.slow': ['yellow', 'bold', 'bgRed'],
'security.breach': ['red', 'bold', 'underline', 'bgYellow'],
// Wildcards for pattern matching
'*.error': ['red'],
'performance.*': ['magenta'],
'*.slow': ['bold', 'bgYellow']
}
}
});
// Theme selection follows specificity
logger.error('Auth failed', { tags: ['api.error'] }); // Uses 'api.error' style
logger.warn('Slow query', { tags: ['database.slow'] }); // Uses 'database.slow' style
logger.info('Request', { tags: ['api.request'] }); // Falls back to 'api' style
Combine tags with themes for automatic visual categorization:
const logger = new Logger({
theme: {
tags: {
// Exact match
'error': ['red', 'bold'],
'warning': ['yellow'],
'success': ['green', 'bold'],
// Hierarchical matching
'api': ['cyan', 'bold'],
'api.request': ['cyan'],
'api.response': ['brightCyan'],
'api.error': ['red', 'bold'],
// Category styling
'database': ['yellow'],
'database.slow': ['yellow', 'bold', 'bgRed'],
'cache': ['magenta'],
'security': ['red', 'bold', 'underline']
}
}
});
// Automatic styling based on tags
logger.info('Request received', { tags: ['api.request'] }); // Cyan
logger.warn('Slow query', { tags: ['database.slow'] }); // Yellow on red
logger.error('Auth failed', { tags: ['security', 'api.error'] }); // Red, bold, underline
import { TagManager } from 'magiclogger';
const tagManager = new TagManager();
// Generate tags from file paths
const tags = tagManager.fromPath('src/api/v2/users/create.ts');
// Result: ['src', 'api', 'v2', 'users', 'create']
// Normalize and validate tags
const normalized = tagManager.normalize(['API', 'User-Auth', 'OAuth/2.0']);
// Result: ['api', 'user-auth', 'oauth-2-0']
// Filter logs by tags
const logs = [/* array of log entries */];
const apiLogs = logs.filter(log =>
tagManager.matches(log.tags, 'api.*')
);
Define context types for consistency.
interface RequestContext {
requestId: string;
userId?: string;
method: string;
path: string;
duration?: number;
}
const logger = new Logger<RequestContext>();
logger.info('Request completed', {
requestId: 'req-123',
method: 'GET',
path: '/api/users',
duration: 45
});
MagicLogger supports console-like variadic arguments while maintaining structured output:
import { Logger, meta, err } from 'magiclogger';
const logger = new Logger();
// Print like console.log
logger.info('Data:', { a: 1, b: 2 });
// Attach metadata for transports (not printed)
logger.info('Saved user', user, meta({ requestId, userId }));
// Structured error handling
logger.error('Failed to save', err(new Error('boom')), meta({ requestId }));
MagicLogger provides comprehensive validation for both context and tags, ensuring data quality and preventing malformed logs from polluting your logging infrastructure.
Define schemas to enforce structure and types for your log data:
import { Logger, ContextManager, TagManager } from 'magiclogger';
import type { ObjectSchema } from 'magiclogger/validation';
// Define a schema for context validation
const contextSchema: ObjectSchema = {
type: 'object',
properties: {
userId: {
type: 'string',
format: 'uuid',
optional: false
},
email: {
type: 'string',
format: 'email',
transform: (v) => v.toLowerCase()
},
age: {
type: 'number',
min: 0,
max: 150
},
roles: {
type: 'array',
items: { type: 'string' },
minItems: 1
},
metadata: {
type: 'object',
additionalProperties: true
}
},
required: ['userId', 'email']
};
// Configure validation behavior
const contextManager = new ContextManager({
schema: contextSchema,
schemaValidationMode: 'warn', // 'throw' | 'warn' | 'silent'
enableValidation: true
});
Control how validation failures are handled:
// Strict mode - throws errors on validation failure
const strictLogger = new Logger({
contextManager: new ContextManager({
schema: userSchema,
schemaValidationMode: 'throw' // Fails fast
})
});
// Warning mode - logs warnings but continues
const warnLogger = new Logger({
contextManager: new ContextManager({
schema: userSchema,
schemaValidationMode: 'warn' // Logs warnings to console
})
});
// Silent mode - silently drops invalid data
const silentLogger = new Logger({
contextManager: new ContextManager({
schema: userSchema,
schemaValidationMode: 'silent' // No errors or warnings
})
});
Listen to validation events for custom handling:
const contextManager = new ContextManager({
schema: contextSchema,
schemaValidationMode: 'silent'
});
// Listen for validation failures
contextManager.on('schemaValidationFailed', ({ result, context }) => {
// Custom handling - send to error tracking
errorTracker.report('Invalid log context', {
errors: result.errors,
context: context
});
// Or increment metrics
metrics.increment('logs.validation.failed', {
errorCount: result.errors.length
});
});
// Listen for successful validations
contextManager.on('validated', (context) => {
metrics.increment('logs.validation.success');
});
const contextManager = new ContextManager({
// Structure limits
maxDepth: 5, // Maximum nesting depth
maxProperties: 50, // Maximum properties per object
// Security
sanitizeMode: 'strict', // Remove sensitive data
freezeContext: true, // Prevent mutations
// Custom validation rules
enableValidation: true
});
// Set validation rules programmatically
contextManager.setValidationRules({
required: ['requestId', 'userId'],
types: {
requestId: 'string',
userId: 'string',
timestamp: 'number',
success: 'boolean'
},
custom: (context) => {
// Custom validation logic
if (context.userId && context.userId === 'admin') {
return context.adminToken !== undefined;
}
return true;
}
});
const tagManager = new TagManager({
maxTags: 10, // Maximum number of tags
maxTagLength: 50, // Maximum length per tag
allowedPattern: /^[a-z0-9.-]+$/, // Regex pattern
// Schema for structured tags
schema: {
type: 'array',
items: {
type: 'string',
pattern: /^[a-z]+(\.[a-z]+)*$/, // Hierarchical pattern
maxLength: 50
},
maxItems: 10,
uniqueItems: true
},
schemaValidationMode: 'warn'
});
MagicLogger supports comprehensive schema types:
// String validation
const stringSchema = {
type: 'string',
minLength: 3,
maxLength: 100,
pattern: /^[A-Z]/, // Must start with capital
format: 'email', // Predefined formats
enum: ['admin', 'user'], // Allowed values
trim: true, // Auto-trim whitespace
toLowerCase: true // Auto-lowercase
};
// Number validation
const numberSchema = {
type: 'number',
min: 0,
max: 100,
integer: true, // Must be integer
positive: true, // Must be positive
multipleOf: 5 // Must be multiple of 5
};
// Array validation
const arraySchema = {
type: 'array',
items: { type: 'string' },
minItems: 1,
maxItems: 10,
uniqueItems: true // No duplicates
};
// Object validation
const objectSchema = {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'number' }
},
required: ['name'],
additionalProperties: false, // No extra props
minProperties: 1,
maxProperties: 10
};
// Union types
const unionSchema = {
type: 'union',
schemas: [
{ type: 'string' },
{ type: 'number' }
]
};
Automatic sanitization of sensitive data:
const contextManager = new ContextManager({
sanitizeMode: 'strict', // 'none' | 'basic' | 'strict' | 'custom'
// Custom sanitization function
sanitize: (value) => {
if (typeof value === 'string') {
// Redact credit card numbers
return value.replace(/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, '****-****-****-****');
}
return value;
}
});
// Automatic sensitive key detection
const context = {
userId: '123',
password: 'secret123', // Automatically redacted
creditCard: '4111-1111-1111-1111', // Automatically redacted
apiToken: 'xyz789', // Automatically redacted
data: 'safe-data'
};
// Result after sanitization:
// {
// userId: '123',
// password: '***',
// creditCard: '***',
// apiToken: '***',
// data: 'safe-data'
// }
Validation is designed to be efficient and tree-shakeable, its components loaded only when schemas are defined.
Use appropriate validation modes:
throw
for development and testingwarn
for staging environmentssilent
for production (with event listeners)Define schemas at initialization:
// Good - schema defined once
const schema = { /* ... */ };
const logger = new Logger({ contextManager: new ContextManager({ schema }) });
// Avoid - schema defined per log
logger.info('message', validateSchema({ /* ... */ }));
Monitor validation failures:
contextManager.on('schemaValidationFailed', ({ result }) => {
monitoring.recordValidationFailure(result.errors);
});
Use transforms for data normalization:
const schema = {
type: 'object',
properties: {
email: {
type: 'string',
transform: (v) => v.toLowerCase().trim()
},
timestamp: {
type: 'number',
transform: (v) => Math.floor(v) // Remove decimals
}
}
};
Combine with TypeScript for compile-time safety:
interface UserContext {
userId: string;
email: string;
roles: string[];
}
const logger = new Logger<UserContext>();
// TypeScript ensures compile-time type safety
// Schema ensures runtime validation
Extensions are opt-in for specialized needs:
import { Redactor, Sampler, RateLimiter } from 'magiclogger/extensions';
const logger = new Logger({
// PII Redaction
redactor: new Redactor({ preset: 'strict' }),
// Statistical sampling (10% of logs)
sampler: new Sampler({ rate: 0.1 }),
// Rate limiting (1000/minute)
rateLimiter: new RateLimiter({ max: 1000, window: 60000 })
});
MagicLogger delivers 170K ops/sec with styled output and works in both Node.js and browsers. While ~50% slower than Pino (374K ops/sec), this is by design - every log includes:
The performance trade-off is purposeful: complete observability data in every log. Choose MagicLogger when you need structured logging with visual debugging that works everywhere.
Note: Future versions may offer a "performance mode" without default structured logging.
Logger | Ops/sec | Avg (ms) | P50 | P95 | P99 | Max |
---|---|---|---|---|---|---|
Pino | 560,285 | 0.002 | 0.001 | 0.003 | 0.004 | 4.808 |
Winston (Plain) | 306,954 | 0.003 | 0.001 | 0.003 | 0.049 | 0.633 |
MagicLogger (Sync) | 269,587 | 0.003 | 0.001 | 0.003 | 0.008 | 4.547 |
MagicLogger (Async) | 165,694 | 0.006 | 0.003 | 0.007 | 0.032 | 5.305 |
Bunyan | 84,515 | 0.012 | 0.008 | 0.020 | 0.045 | 8.435 |
Performance Notes:
Logger | Ops/sec | Avg (ms) | P50 | P95 | P99 | Max |
---|---|---|---|---|---|---|
Winston (Sync + Styled) | 446,027 | 0.002 | 0.001 | 0.002 | 0.035 | 4.662 |
Pino (Pretty) | 274,431 | 0.004 | 0.003 | 0.004 | 0.008 | 0.097 |
MagicLogger (Async + Styles) | 116,404 | 0.008 | 0.005 | 0.010 | 0.034 | 8.074 |
Bunyan (Styled) | 99,468 | 0.010 | 0.007 | 0.017 | 0.029 | 6.036 |
MagicLogger (Sync + Styles) | 80,502 | 0.012 | 0.005 | 0.016 | 0.031 | 8.340 |
Performance benchmarks are run manually via npm run perf:update
when performance improvements are made - see scripts/performance/
Default Mode (Logger/SyncLogger):
<style>text</>
markupAsyncLogger with Worker Threads (optional):
worker.enabled: true
for heavy styling workloadsArchitecture Choices:
Why AsyncLogger is Recommended:
When to use SyncLogger (rare):
See benchmark methodology and architecture docs.
interface LoggerOptions {
// Basic configuration
id?: string;
tags?: string[];
context?: Record<string, unknown>;
verbose?: boolean;
useColors?: boolean; // Enable colored output (default: true)
useConsole?: boolean; // Add console transport automatically (default: true, set to false to disable)
// Styling & themes
theme?: string | ThemeDefinition;
// Performance features
buffer?: BufferOptions;
sampling?: SamplingOptions;
rateLimit?: RateLimitOptions;
// Security
redaction?: RedactionOptions;
// Transports
transports?: Transport[];
}
// Logging methods
logger.debug(message, meta?)
logger.info(message, meta?)
logger.warn(message, meta?)
logger.error(message, meta?)
logger.success(message, meta?)
// Styling
logger.s // Chainable style API
logger.fmt // Template literal API
// Visual elements
logger.header(text, styles?)
logger.separator(char, length)
logger.progressBar(percent, width?)
logger.table(data)
logger.diff(label, oldObj, newObj)
// Management
logger.flush() // Force flush buffers
logger.close() // Graceful shutdown
logger.getStats() // Performance metrics
📚 View Documentation | Getting Started | API Reference
npm run docs # Start docs dev server with live reload
npm run docs:build # Build production docs
For development setup and build instructions, see Development Guide and Build Instructions.
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
MIT © Manic.agency
Developed and sponsored by Manic.agency
File | Format | Raw Size | Gzip |
---|---|---|---|
index.cjs |
CJS | 10.7 kB | 2.35 kB |
index.js |
ESM | 6.49 kB | 1.94 kB |
index.d.ts |
Types | 180 kB | 38.2 kB |
Scenario | Size |
---|---|
Core (bare minimum) | 47.1 kB |
Core + Console Transport | 47.1 kB |
Core + File Transport | 47.1 kB |
Core + HTTP Transport | 49.7 kB |
Core + All Basic Transports | 51.2 kB |
Generated via scripts/analyze-build.js
.
File | Format | Raw Size | Gzip |
---|---|---|---|
index.cjs |
CJS | 10.7 kB | 2.35 kB |
index.js |
ESM | 6.49 kB | 1.94 kB |
index.d.ts |
Types | 180 kB | 38.2 kB |
Scenario | Size |
---|---|
Core (bare minimum) | 47.1 kB |
Core + Console Transport | 47.1 kB |
Core + File Transport | 47.1 kB |
Core + HTTP Transport | 49.7 kB |
Core + All Basic Transports | 51.2 kB |
Generated via scripts/analyze-build.js
.