npm package: @lazhus/kg-central-logger
Description: Central logging system with RabbitMQ support and fallback file logging
Author: Sendeo
Latest version: 1.0.0

# @sendeo/central-logger

Central logging system with RabbitMQ support and fallback file logging. This package is a Node.js/TypeScript implementation inspired by the .NET CentralLogging system.

## Features

- 🚀 Asynchronous logging with internal queue
- 🐰 RabbitMQ integration with topic exchange
- 📁 Automatic fallback to file logging on RabbitMQ failure
- 🔄 Automatic reconnection on connection loss
- 📊 Log levels (Trace, Debug, Information, Warning, Error, Critical)
- 🏷️ Enrichers support
- 🎯 Scope support with AsyncLocalStorage
- ⚙️ Configurable channel bounds and parallelism
- 🎨 Category-based log level filtering

## Installation

```bash
npm install @sendeo/central-logger
```

## Quick Start

```typescript
import { createCentralLogger, LogLevel } from '@sendeo/central-logger';

// Create logger provider
const provider = createCentralLogger(
  'amqp://localhost:5672',
  (config) => {
    config
      .setLogKey('my-service')
      .setExchangeName('logs-exchange')
      .setDefaultLogLevel(LogLevel.Information)
      .addEnricher('Environment', 'production')
      .addEnricher('ServiceName', 'my-service');
  }
);

// Create logger instance
const logger = provider.createLogger('MyController');

// Log messages
logger.info('Application started', { version: '1.0.0' });
logger.error('Something went wrong', new Error('Test error'), { userId: '123' });

// Use scopes
const scope = logger.beginScope({ requestId: 'abc-123' });
logger.info('Processing request'); // Will include requestId in properties
scope.dispose();

// Cleanup on shutdown
await provider.dispose();
```

## Configuration

### CentralLoggerConfiguration

```typescript
const config = new CentralLoggerConfiguration()
  .setLogKey('my-service')              // Required: Service identifier
  .setExchangeName('logs-exchange')     // Required: RabbitMQ exchange name
  .setConnectionKey('default')          // Optional: Connection identifier
  .setIsSpecific()                      // Optional: Use specific routing
  .setChannelBound(20000)               // Optional: Max queue size (default: 20000)
  .setMaxParallelism(20)                // Optional: Parallel processing (default: 20)
  .setFailedLogsBaseFolder('logs')      // Optional: Fallback folder (default: 'CentralFailedLogs')
  .setDefaultLogLevel(LogLevel.Info)    // Optional: Default log level
  .setMinimumLogLevel('MyController', LogLevel.Debug) // Optional: Category-specific level
  .addEnricher('key', 'value');         // Optional: Add enrichers
```

## Usage Examples

### Basic Logging

```typescript
const logger = provider.createLogger('UserService');

logger.trace('Trace message');
logger.debug('Debug message');
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message', new Error('Something failed'));
logger.critical('Critical error', new Error('System failure'));
```

### Logging with Properties

```typescript
logger.info('User logged in', {
  userId: '12345',
  username: 'john.doe',
  ipAddress: '192.168.1.1'
});
```

### Using Scopes

```typescript
// Scopes add properties to all logs within the scope
const scope = logger.beginScope({
  requestId: 'req-123',
  correlationId: 'corr-456'
});

logger.info('Starting process');  // Includes requestId and correlationId
logger.info('Process completed'); // Includes requestId and correlationId

scope.dispose(); // Clean up scope
```

### Nested Scopes

```typescript
const outerScope = logger.beginScope({ requestId: 'req-123' });

logger.info('Outer scope');

const innerScope = logger.beginScope({ operationId: 'op-456' });
logger.info('Inner scope'); // Includes both requestId and operationId
innerScope.dispose();

logger.info('Back to outer'); // Only includes requestId
outerScope.dispose();
```

### Category-based Log Levels

```typescript
const provider = createCentralLogger('amqp://localhost', (config) => {
  config
    .setLogKey('my-service')
    .setExchangeName('logs')
    .setDefaultLogLevel(LogLevel.Information)
    .setMinimumLogLevel('DatabaseService', LogLevel.Debug)
    .setMinimumLogLevel('CacheService', LogLevel.Warning);
});

const dbLogger = provider.createLogger('DatabaseService');
dbLogger.debug('This will be logged'); // Debug enabled for DatabaseService

const cacheLogger = provider.createLogger('CacheService');
cacheLogger.info('This will NOT be logged'); // Only Warning+ for CacheService
cacheLogger.warn('This will be logged');
```

## Log Entry Structure

Each log entry contains:

```typescript
{
  logKey: string;              // Service identifier
  timestamp: Date;             // Log timestamp
  level: string;               // Log level (Trace, Debug, Information, etc.)
  source?: string;             // Logger category name
  eventId?: number;            // Optional event ID
  eventName?: string;          // Optional event name
  message?: string;            // Log message
  messageTemplate?: string;    // Message template with placeholders
  exception?: {                // Exception details (if any)
    type?: string;
    message?: string;
    stackTrace?: string;
    innerException?: {...};
  };
  enrichers: Record<string, string>;        // Global enrichers
  properties?: Record<string, string|null>; // Log properties + scope properties
}
```

## RabbitMQ Integration

### Routing Key Format

Logs are published to RabbitMQ with the following routing key format:

```
project.{logKey}.{suffix}
```

- `logKey`: Your service identifier (lowercase)
- `suffix`: Either `specific` or `general` (based on `setIsSpecific()`)

Example: `project.my-service.general`

### Exchange Configuration

The logger creates a **topic exchange** with durable flag. Make sure your RabbitMQ consumers bind to this exchange with appropriate routing key patterns.

### Automatic Reconnection

The logger automatically attempts to reconnect to RabbitMQ if the connection is lost. During disconnection, logs are written to fallback files.

## Fallback File Logging

When RabbitMQ is unavailable, logs are automatically written to files in the configured folder (default: `CentralFailedLogs`).

Files are named: `failed-logs-{timestamp}.json`

Each line in the file is a JSON-serialized log entry.

## Performance Considerations

- **Internal Queue**: Logs are queued internally and processed asynchronously
- **Bounded Channel**: Queue size is limited (default: 20,000). Oldest logs are dropped when full
- **Parallel Processing**: Multiple logs are published in parallel (default: 20)
- **Non-blocking**: Logging operations never block the main thread

## Cleanup

Always dispose the provider when shutting down your application:

```typescript
process.on('SIGTERM', async () => {
  await provider.dispose();
  process.exit(0);
});
```

## TypeScript Support

This package is written in TypeScript and includes full type definitions.

## License

MIT

## Credits

Inspired by the Sendeo.Core.CentralLogging .NET library.