npm package: @rcommerz/logger-express
Description: Production-ready structured logging for Express/Node.js microservices with OpenTelemetry support
Author: RCOMMERZ
Latest version: 1.0.0

# logger-express

[![npm version](https://badge.fury.io/js/%40rcommerz%2Flogger-express.svg)](https://www.npmjs.com/package/@rcommerz/logger-express)
[![CI Tests](https://github.com/rcommerz/logger-express/actions/workflows/test.yml/badge.svg)](https://github.com/rcommerz/logger-express/actions/workflows/test.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue.svg)](https://www.typescriptlang.org/)
[![Test Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/rcommerz/logger-express)

Production-ready structured logging package for Node.js/Express microservices with OpenTelemetry support and LGTM stack compatibility.

## Features

- ✅ **Singleton Pattern** - Initialize once, use everywhere
- ✅ **Structured JSON Logging** - Always outputs valid JSON
- ✅ **OpenTelemetry Integration** - Automatic trace_id and span_id extraction
- ✅ **Express Middleware** - Automatic HTTP request/response logging
- ✅ **Performance** - Built on Pino (one of the fastest Node.js loggers)
- ✅ **Type-Safe** - Full TypeScript support
- ✅ **Container-Friendly** - Outputs to stdout
- ✅ **Production-Ready** - Minimal dependencies, battle-tested

## Installation

```bash
npm install @rcommerz/logger-express
```

## Quick Start

### 1. Initialize Logger (Do this once in your app startup)

```typescript
import { Logger } from '@rcommerz/logger-express';

Logger.initialize({
  serviceName: 'product-service',
  serviceVersion: '1.2.0',
  env: process.env.NODE_ENV || 'development',
});
```

### 2. Use Logger Anywhere

```typescript
import { Logger } from '@rcommerz/logger-express';

const logger = Logger.getInstance();

// Basic logging
logger.info('Application started');
logger.error('Database connection failed', { error: err });
logger.warn('Cache miss', { cache_key: 'user:123' });
logger.debug('Processing request', { request_id: 'abc-123' });

// With dynamic context
logger.info('Order created', {
  order_id: 'ORD-12345',
  user_id: 'USR-999',
  total_amount: 199.99,
  items_count: 3,
});

// Security logging
logger.security('Failed login attempt', {
  user_id: 'USR-456',
  ip: '192.168.1.100',
  reason: 'Invalid credentials',
});

// Audit logging
logger.audit('User permission changed', {
  user_id: 'USR-789',
  admin_id: 'ADM-001',
  old_role: 'user',
  new_role: 'admin',
});
```

### 3. HTTP Middleware (Express)

```typescript
import express from 'express';
import { Logger, httpLogger, errorLogger } from '@rcommerz/logger-express';

const app = express();

// Initialize logger
Logger.initialize({
  serviceName: 'api-gateway',
  serviceVersion: '1.0.0',
  env: process.env.NODE_ENV || 'development',
});

// Add HTTP logging middleware
app.use(httpLogger({
  excludePaths: ['/health', '/metrics'],
  includeHeaders: false,
  includeBody: false, // Be careful with sensitive data
}));

// Your routes
app.get('/api/products', (req, res) => {
  res.json({ products: [] });
});

// Error logging middleware (add after routes)
app.use(errorLogger());

app.listen(3000);
```

## Example Output

### Standard Log

```json
{
  "@timestamp": "2026-02-22T10:30:45.123Z",
  "log.level": "INFO",
  "log_type": "normal",
  "service.name": "product-service",
  "service.version": "1.2.0",
  "env": "production",
  "host.name": "pod-product-abc123",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "00f067aa0ba902b7",
  "message": "Order created",
  "order_id": "ORD-12345",
  "user_id": "USR-999",
  "total_amount": 199.99,
  "items_count": 3
}
```

### HTTP Request Log

```json
{
  "@timestamp": "2026-02-22T10:30:46.456Z",
  "log.level": "INFO",
  "log_type": "http",
  "service.name": "api-gateway",
  "service.version": "1.0.0",
  "env": "production",
  "host.name": "pod-gateway-xyz789",
  "trace_id": "3ad45f8b21c34e2a8d41ba6e3f9c0412",
  "span_id": "91f23ab4cd5e6789",
  "message": "GET /api/products 200",
  "method": "GET",
  "path": "/api/products",
  "status_code": 200,
  "duration_ms": 45,
  "user_agent": "Mozilla/5.0",
  "ip": "10.0.1.25"
}
```

### Error Log with Stack Trace

```json
{
  "@timestamp": "2026-02-22T10:30:47.789Z",
  "log.level": "ERROR",
  "log_type": "error",
  "service.name": "payment-service",
  "service.version": "2.1.0",
  "env": "production",
  "host.name": "pod-payment-def456",
  "trace_id": "7cd89e12f43b4a9f8e21dc7a5b6f3028",
  "span_id": "12a34b56c78d90ef",
  "message": "Payment processing failed",
  "error_message": "Insufficient funds",
  "error_type": "PaymentError",
  "error_stack": "PaymentError: Insufficient funds\n    at processPayment (/app/src/payment.ts:45:11)\n    ...",
  "payment_id": "PAY-67890",
  "user_id": "USR-111",
  "amount": 500.00
}
```

## API Reference

### Logger Methods

#### `info(message: string, context?: LogContext): void`

Log informational messages.

#### `error(message: string, context?: LogContext): void`

Log error messages. Automatically extracts Error objects.

#### `warn(message: string, context?: LogContext): void`

Log warning messages.

#### `debug(message: string, context?: LogContext): void`

Log debug messages (only in debug mode).

#### `security(message: string, context?: LogContext): void`

Log security-related events (log_type = "security").

#### `audit(message: string, context?: LogContext): void`

Log audit trail events (log_type = "audit").

#### `http(message: string, context?: LogContext): void`

Log HTTP-specific events (log_type = "http").

### Middleware Options

#### `httpLogger(options)`

- `excludePaths?: string[]` - Paths to exclude from logging (e.g., `/health`, `/metrics`)
- `includeHeaders?: boolean` - Include request headers in logs (default: false)
- `includeBody?: boolean` - Include request body in logs (default: false, use carefully)

## OpenTelemetry Integration

The logger automatically detects and includes OpenTelemetry trace context when available:

```typescript
import { trace } from '@opentelemetry/api';

// Your OpenTelemetry setup...
const tracer = trace.getTracer('my-service');

// Start a span
const span = tracer.startSpan('process-order');

// Logger will automatically include trace_id and span_id
logger.info('Processing order', { order_id: '123' });

span.end();
```

## Best Practices

### ✅ DO

- Initialize logger once at application startup
- Use appropriate log levels (info, error, warn, debug)
- Include relevant context (user_id, order_id, etc.)
- Use `logger.security()` for authentication/authorization events
- Use `logger.audit()` for compliance-tracked actions
- Filter sensitive data before logging (passwords, tokens, credit cards)
- Use `excludePaths` for health checks and metrics endpoints

### ❌ DON'T

- Log sensitive information (passwords, credit cards, tokens)
- Log at debug level in production (disable via LOG_LEVEL env var)
- Initialize logger multiple times
- Use `includeBody: true` without sanitizing sensitive fields
- Create logger instances with `new Logger()` (it's private)

## Environment Variables

- `LOG_LEVEL` - Set minimum log level: `debug`, `info`, `warn`, `error` (default: `info`)
- `NODE_ENV` - Application environment: `development`, `staging`, `production`

## LGTM Stack Compatibility

This logger is designed for:

- **Loki** - Structured JSON logs with standardized labels
- **Grafana** - Easy visualization with consistent field names
- **Tempo** - Automatic trace correlation via trace_id/span_id
- **Prometheus** - Metrics can be derived from log patterns

### Example Loki Query

```logql
{service_name="product-service"} | json | log_type="http" | status_code >= 500
```

### Example Grafana Dashboard Query

```logql
sum(rate({service_name="api-gateway"} | json | log_type="http" [5m])) by (status_code)
```

## TypeScript Support

Full TypeScript support with exported types:

```typescript
import { Logger, LoggerConfig, LogContext, LogType } from '@rcommerz/logger-express';
```

## License

MIT

## Support

For issues and questions, please open an issue in the repository.