Logging

Overview

Katal's logging system provides a flexible, extensible logging solution with multiple destinations and log levels.

Features

  • Multiple log levels (DEBUG, INFO, WARN, ERROR)
  • Multiple concurrent destinations (Console, File)
  • Colored console output
  • Structured logging with context
  • Error stack traces
  • Atomic file writes
  • Asynchronous operation

Basic Usage

import { LoggingIntegration, LogLevel } from 'katal/logging';

const logger = new LoggingIntegration()
    .setMinLevel(LogLevel.INFO)
    .toConsole()
    .toFile('./logs/app.log');

// Basic logging
await logger.info('Application started');
await logger.error('Error occurred', new Error('Something went wrong'));

// Logging with context
await logger.info('User action', {
    userId: 123,
    action: 'login'
});

Log Levels

enum LogLevel {
    DEBUG = "debug",
    INFO = "info",
    WARN = "warn",
    ERROR = "error"
}

Destinations

Console Destination

Provides colored output to the console with formatted messages:

const logger = new LoggingIntegration()
    .toConsole();

// Outputs with color based on level:
// [2025-10-02T12:34:56.789Z] INFO  Message
// [2025-10-02T12:34:56.789Z] ERROR Error message

File Destination

Writes logs to a file with atomic operations:

const logger = new LoggingIntegration()
    .toFile('./logs/app.log');

// Creates file if it doesn't exist
// Appends logs atomically
// Handles concurrent writes safely

Custom Destinations

Implement your own destinations by implementing the LogDestination interface:

interface LogDestination {
    write(entry: LogEntry): Promise<void> | void;
}

interface LogEntry {
    level: LogLevel;
    message: string;
    timestamp: Date;
    context?: Record<string, any>;
    error?: Error;
}

// Example Database Destination
class DatabaseDestination implements LogDestination {
    async write(entry: LogEntry): Promise<void> {
        await db.logs.create({
            level: entry.level,
            message: entry.message,
            timestamp: entry.timestamp,
            context: entry.context,
            error: entry.error?.message
        });
    }
}

// Usage
logger.addDestination(new DatabaseDestination());

Error Handling

The logging system handles errors gracefully:

// Destination errors are caught and logged
try {
    await logger.error('Failed operation', error);
} catch (e) {
    // Logging failures don't throw to application code
}

// File system errors are handled
const logger = new LoggingIntegration()
    .toFile('./invalid/path/app.log'); // Will handle directory creation errors

Best Practices

  1. Configure Different Log Levels
// Development
const logger = new LoggingIntegration()
    .setMinLevel(LogLevel.DEBUG)
    .toConsole();

// Production
const logger = new LoggingIntegration()
    .setMinLevel(LogLevel.INFO)
    .toFile('./logs/app.log')
    .toFile('./logs/error.log', { minLevel: LogLevel.ERROR });
  1. Use Structured Logging
await logger.info('API Request', {
    method: 'POST',
    path: '/users',
    duration: 123,
    statusCode: 200
});
  1. Include Error Context
try {
    await someOperation();
} catch (error) {
    await logger.error('Operation failed', error, {
        operationName: 'someOperation',
        input: data
    });
}
  1. Use Method Chaining
const logger = new LoggingIntegration()
    .setMinLevel(LogLevel.INFO)
    .toConsole()
    .toFile('./logs/app.log')
    .addDestination(new CustomDestination());

---

## See Also

- [Observability](OBSERVABILITY.md)
- [Application & Router](CORE.md)