magiclogger - v0.1.0
    Preparing search index...

    magiclogger - v0.1.0

    MagicLogger

    MagicLog

    🌐 Documentation & Website📚 API Reference

    core_gzip core_console_gzip core_transports_gzip

    CI Release Docs
    GitHub Stars npm version codecov
    TypeScript Node.js License

    MagicLogger Terminal Demo

    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:

    • 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
    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.

    • 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

    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:

    1. 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"
      }
    2. Preserve Style Information

      • Extract styles from markup (e.g., <red>text</>)
      • Store as [startIndex, endIndex, style] tuples
      • Keep plain text in message field
    3. Include Required Fields

      • id, timestamp, level, message
      • Optional but recommended: 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.

    1. Use appropriate validation modes:

      • throw for development and testing
      • warn for staging environments
      • silent for production (with event listeners)
    2. 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({ /* ... */ }));
    3. Monitor validation failures:

      contextManager.on('schemaValidationFailed', ({ result }) => {
      monitoring.recordValidationFailure(result.errors);
      });
    4. 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
      }
      }
      };
    5. 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:

    • 180K ops/sec plain text, 170K ops/sec styled output
    • Full MAGIC schema conformity - Structured logging by default
    • OpenTelemetry compatible out of the box - No plugins needed
    • Browser + Node.js - Same API everywhere (unlike Pino/Winston which are Node-only)
    • Similar size to Winston (~42KB vs ~44KB)

    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:

    • MagicLogger achieves 250K+ ops/sec for synchronous plain text logging
    • AsyncLogger delivers 120K+ ops/sec with styled output (only 11.8% overhead)
    • Pre-compiled style patterns cache for common log formats
    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 extraction happens in the main thread before sending to transports
    • Uses efficient regex-based parsing to extract <style>text</> markup
    • Produces plain text + style ranges array for the MAGIC schema
    • LRU cache reduces repeated style generation overhead
    • Deep dive into our style optimization techniques →

    AsyncLogger with Worker Threads (optional):

    • Workers are OFF by default for better latency
    • Enable with worker.enabled: true for heavy styling workloads
    • 4x faster for complex styles but adds IPC overhead for simple logs
    • Recommended only for >10K styled logs/sec

    Architecture Choices:

    • sonic-boom: High-performance async file I/O with internal buffering
    • Fast Path Detection: Unstyled text bypasses style processing entirely
    • Worker Pool Pattern: Reusable worker threads when needed (avoids spawn overhead)

    Why AsyncLogger is Recommended:

    • Non-blocking: Keeps your app responsive even under heavy logging
    • Optimized for styled output: 151K ops/sec with advanced style caching
    • Smart batching: Automatically optimizes for network transports (100-1000 entries)
    • Production-ready: Graceful shutdown, backpressure handling, automatic retries

    When to use SyncLogger (rare):

    • Critical audit logs that must NEVER be lost even under extreme load
    • Regulatory compliance requiring synchronous disk writes
    • Trade-off: Can make your app unresponsive under load

    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.