npm package: @heavybit/logger-ts
Description: A comprehensive TypeScript logging package for Express.js with AOP support, request/response logging middleware, stdout and file logging
Author: Nicholas Kute
Latest version: 1.0.0

# @pesalink/logger-ts

A comprehensive TypeScript logging package for Express.js applications with AOP (Aspect-Oriented Programming) support, request/response logging middleware, stdout and file logging based on environment configuration.

## Features

- 🚀 **TypeScript First**: Full TypeScript support with comprehensive type definitions
- 📝 **Winston-based**: Built on the robust Winston logging library
- 🔄 **Dual Output**: Log to stdout (console), files, or both based on configuration
- 🗂️ **Log Rotation**: Automatic daily log rotation with compression
- 🎯 **AOP Logging**: Aspect-oriented programming support with decorators and utilities
- 🛡️ **Data Sanitization**: Automatic redaction of sensitive fields (passwords, tokens, etc.)
- 🏷️ **Class-based Log Levels**: Different log levels for controllers, services, repositories, etc.
- 🔧 **Environment Config**: Full configuration via environment variables
- 🧩 **Express Middleware**: Ready-to-use logging middleware for Express.js
- 📊 **Structured Logging**: JSON-formatted logs for easy parsing and analysis

## Installation

```bash
npm install @pesalink/logger-ts

# Peer dependency
npm install express
```

## Quick Start

### Basic Usage

```typescript
import express from 'express';
import { initLogger, getLogger, loggingMiddleware } from '@pesalink/logger-ts';

const app = express();

// Initialize logger (reads from environment variables)
const logger = initLogger();

// Add middleware
app.use(express.json());
app.use(loggingMiddleware);

// Use the logger
app.get('/api/users', async (req, res) => {
  logger.info('Fetching users');
  res.json({ users: [] });
});

app.listen(3000, () => {
  logger.info('Server started on port 3000');
});
```

### Custom Configuration

```typescript
import { initLogger, LoggerConfig } from '@pesalink/logger-ts';

const config: Partial<LoggerConfig> = {
  target: 'both', // 'stdout' | 'file' | 'both'
  defaultLevel: 'debug',
  appName: 'my-service',
  console: {
    colorize: true,
    level: 'debug',
  },
  file: {
    directory: 'logs',
    maxSize: '100m',
    maxFiles: '30d',
    zippedArchive: true,
    datePattern: 'YYYY-MM-DD',
    level: 'info',
  },
  classLevels: {
    controllers: 'info',
    services: 'debug',
    repositories: 'error',
  },
  sensitiveFields: ['customSecret'], // Additional fields to redact
};

const logger = initLogger(config);
```

## Environment Variables

```bash
# Log target: 'stdout', 'file', or 'both'
LOG_TARGET=both

# Default log level
LOG_LEVEL=debug

# Console configuration
LOG_COLORIZE=true

# File configuration
LOG_DIR=logs
LOG_MAX_SIZE=50m
LOG_MAX_FILES=30d
LOG_DATE_PATTERN=YYYY-MM-DD
LOG_ZIPPED_ARCHIVE=true

# Class-specific log levels
LOG_LEVEL_CONTROLLERS=info
LOG_LEVEL_SERVICES=debug
LOG_LEVEL_REPOSITORIES=error
LOG_LEVEL_UTILS=warn
LOG_LEVEL_MIDDLEWARE=info
LOG_LEVEL_ROUTES=info

# Application info
APP_NAME=my-app
NODE_ENV=development
HEALTHCHECK_ENDPOINT=/health
```

## API Reference

### Logger Functions

#### `initLogger(config?: Partial<LoggerConfig>): ExtendedLogger`

Initialize the logger singleton. Call once at application startup.

```typescript
const logger = initLogger({
  target: 'both',
  defaultLevel: 'info',
});
```

#### `getLogger(): ExtendedLogger`

Get the logger instance (creates default if not initialized).

```typescript
const logger = getLogger();
logger.info('Hello world');
logger.error('An error occurred', { error: err.message });
```

#### `createLogger(config?: Partial<LoggerConfig>): ExtendedLogger`

Create a new logger instance with custom config (does not replace singleton).

```typescript
const customLogger = createLogger({
  target: 'file',
  file: { directory: 'custom-logs' },
});
```

### Logging Methods

```typescript
const logger = getLogger();

// Standard Winston methods
logger.error('Error message', { metadata: 'value' });
logger.warn('Warning message');
logger.info('Info message');
logger.debug('Debug message');
logger.verbose('Verbose message');

// Extended methods
logger.logWithMeta('info', 'Message', { custom: 'data' });
logger.logRequest({
  message: 'Request completed',
  method: 'GET',
  url: '/api/users',
  statusCode: 200,
  responseTime: '50ms',
});
```

### Middleware

#### `loggingMiddleware`

Default logging middleware with standard configuration.

```typescript
app.use(loggingMiddleware);
```

#### `createLoggingMiddleware(options: MiddlewareOptions)`

Create custom logging middleware.

```typescript
app.use(createLoggingMiddleware({
  skipEndpoints: ['/health', '/metrics'],
  logRequestBody: true,
  logResponseBody: false,
  requestIdGenerator: () => uuid(),
  additionalFields: (req) => ({
    userId: req.user?.id,
  }),
}));
```

#### `errorLoggingMiddleware`

Error logging middleware for Express error handling.

```typescript
app.use(errorLoggingMiddleware);
```

#### `morganStyleMiddleware(format)`

Morgan-style request logging.

```typescript
app.use(morganStyleMiddleware('dev')); // 'combined' | 'common' | 'dev' | 'short' | 'tiny'
```

### AOP (Aspect-Oriented Programming)

#### Using Decorators

```typescript
import { LogClass, LogMethod } from '@pesalink/logger-ts';

@LogClass(['constructor'], ['getAll']) // Exclude constructor, only log errors for getAll
class UserController {
  async getAll(req, res) {
    // Only errors will be logged
  }

  @LogMethod('UserController', 'create', 'around', 'controllers')
  async create(req, res) {
    // Full before/after logging
  }
}
```

#### Using `logMethodsWithAOP`

```typescript
import { logMethodsWithAOP } from '@pesalink/logger-ts';

class ProductService {
  async getAll(req, res) { /* ... */ }
  async create(req, res) { /* ... */ }
}

logMethodsWithAOP(
  ProductService.prototype,
  ['constructor'],        // Excluded methods
  ['getAll'],             // Only log errors for these
  'ProductService',       // Class name
  'services',             // Class type
  'around'                // Advice type
);
```

#### Using `withLogging` Wrapper

```typescript
import { withLogging } from '@pesalink/logger-ts';

const handler = async (req, res) => {
  res.json({ data: [] });
};

app.get('/api/items', withLogging(handler, 'getItems', 'ItemController'));
```

#### Using `wrapRoutesWithLogging`

```typescript
import { Router } from 'express';
import { wrapRoutesWithLogging } from '@pesalink/logger-ts';

const router = Router();

router.get('/', handler1);
router.post('/', handler2);

// Wrap all routes in the router
wrapRoutesWithLogging(router, 'ApiRouter', 'routes', ['GET /']);

app.use('/api', router);
```

### Sanitization

```typescript
import { sanitizeData, sanitizeHeaders, prepareForLogging } from '@pesalink/logger-ts';

// Sanitize object (redacts sensitive fields)
const safe = sanitizeData({
  username: 'john',
  password: 'secret123', // -> [REDACTED]
  token: 'abc123',       // -> [REDACTED]
});

// Sanitize headers
const safeHeaders = sanitizeHeaders(req.headers);

// Prepare for logging with truncation
const truncated = prepareForLogging(largeObject, {
  truncateLength: 1000,
});
```

### Configuration Functions

```typescript
import {
  initConfig,
  getConfig,
  resetConfig,
  getLogLevelForClass,
} from '@pesalink/logger-ts';

// Initialize configuration
initConfig({ target: 'both' });

// Get current config
const config = getConfig();

// Get log level for a class type
const level = getLogLevelForClass(config, 'controllers'); // 'info'

// Reset configuration (useful for testing)
resetConfig();
```

## Log Output Format

### Console (Structured)

```
2024-01-15T10:30:45.123+0000 | app=my-service | className=UserController | OperationName=getUser | Method=GET | Endpoint=/api/users/1 | LogLevel=info | Payload={} | Message=Request completed | RequestId=abc-123 | ResponseTime=45ms | StatusCode=200
```

### File (JSON)

```json
{
  "timestamp": "2024-01-15T10:30:45.123+0000",
  "level": "info",
  "app": "my-service",
  "environment": "production",
  "className": "UserController",
  "methodName": "getUser",
  "method": "GET",
  "url": "/api/users/1",
  "requestId": "abc-123",
  "statusCode": 200,
  "responseTime": "45ms",
  "message": "Request completed"
}
```

## Log Files

When file logging is enabled, logs are stored in the configured directory with the following structure:

```
logs/
├── 2024-01-15.log        # All logs for the day
├── 2024-01-15-error.log  # Error-only logs
├── 2024-01-14.log.gz     # Compressed archived logs
└── 2024-01-14-error.log.gz
```

## Best Practices

### 1. Initialize Early

```typescript
// At the top of your entry file
import { initLogger } from '@pesalink/logger-ts';
const logger = initLogger();
```

### 2. Use Appropriate Log Levels

```typescript
logger.error('Database connection failed', { error: err.message }); // Errors
logger.warn('Rate limit approaching', { current: 90, max: 100 });   // Warnings
logger.info('User logged in', { userId: '123' });                    // Info
logger.debug('Cache hit', { key: 'user:123' });                      // Debug
```

### 3. Include Context

```typescript
logger.info('Processing payment', {
  transactionId: 'txn-123',
  userId: 'user-456',
  amount: 100.00,
  currency: 'USD',
});
```

### 4. Handle Errors Properly

```typescript
try {
  await riskyOperation();
} catch (error) {
  logger.error('Operation failed', {
    error: error.message,
    stack: error.stack,
    context: { operationId: '123' },
  });
  throw error;
}
```

### 5. Use Class-Specific Levels

Configure verbose logging for debugging specific layers:

```bash
LOG_LEVEL_SERVICES=debug
LOG_LEVEL_REPOSITORIES=error  # Only log database errors
```

## Examples

See the `examples/` directory for complete examples:

- [basic-usage.ts](./examples/basic-usage.ts) - Basic Express setup
- [aop-usage.ts](./examples/aop-usage.ts) - AOP decorators and utilities
- [external-service.ts](./examples/external-service.ts) - Microservice integration

## TypeScript Support

All types are exported from the package:

```typescript
import type {
  LoggerConfig,
  LogLevel,
  ClassType,
  LogTarget,
  ExtendedLogger,
  RequestLogEntry,
  AOPContext,
  MiddlewareOptions,
} from '@pesalink/logger-ts';
```

## License

MIT

## Author

Nicholas Kute