@neoma/logging
Version:
Great logging for NestJs
535 lines (432 loc) • 14.5 kB
Markdown
# /logging
High-performance, production-ready logging for NestJS applications powered by Pino. Provides excellent developer experience with request-scoped loggers, field redaction, and seamless NestJS integration.
## Why /logging?
- 🚀 **Drop-in replacement** for NestJS built-in logger
- 🎯 **Request-scoped logging** with automatic request context
- 🔧 **Global context configuration** for application metadata
- 🔒 **Field redaction** for sensitive data protection
- ⚡ **High performance** with Pino under the hood
- 🛠️ **Excellent DX** with native NestJS patterns
- 📝 **Comprehensive testing** with memory buffer approach
## Installation
```bash
npm install /logging pino
```
## Basic Usage
### Quick Start
Replace your existing NestJS logger with ApplicationLoggerService:
```typescript
import { LoggingModule, ApplicationLoggerService } from '@neoma/logging'
import { Module } from '@nestjs/common'
export class AppModule {}
```
### Using the Logger
```typescript
import { Injectable } from '@nestjs/common'
import { ApplicationLoggerService } from '@neoma/logging'
export class UserService {
constructor(private readonly logger: ApplicationLoggerService) {}
createUser(userData: any) {
this.logger.log('Creating new user', { userId: userData.id })
try {
// ... user creation logic
this.logger.log('User created successfully', { userId: userData.id })
} catch (error) {
this.logger.error('Failed to create user', {
userId: userData.id,
error: error.message
})
throw error
}
}
}
```
## Configuration
### Log Levels
Configure the minimum log level to capture:
```typescript
LoggingModule.forRoot({
logLevel: 'warn' // Only log warnings, errors, and fatal messages
})
```
Available levels (from most to least verbose):
- `verbose` (maps to Pino `trace`)
- `debug` - **Enables automatic request/response logging**
- `log` (maps to Pino `info`) - **default**
- `warn`
- `error`
- `fatal`
### Field Redaction
Protect sensitive data by configuring field redaction:
```typescript
LoggingModule.forRoot({
logRedact: [
'password', // Redact any top-level password field
'user.ssn', // Redact nested fields with dot notation
'*.apiKey', // Redact apiKey from any object
'tokens.*.secret', // Redact secret from any object under tokens
'medical.*' // Redact all fields under medical object
]
})
```
**Before redaction:**
```typescript
logger.log('User login', {
username: 'john_doe',
password: 'secret123',
profile: {
email: 'john@example.com',
apiKey: 'sk-1234567890'
}
})
```
**After redaction:**
```json
{
"level": 30,
"msg": "User login",
"username": "john_doe",
"password": "[REDACTED]",
"profile": {
"email": "john@example.com",
"apiKey": "[REDACTED]"
}
}
```
### Log Context
Add global context that gets included with every log entry:
```typescript
LoggingModule.forRoot({
logContext: {
service: 'user-api',
version: '1.2.3',
environment: 'production'
}
})
```
**Result in logs:**
```json
{
"level": 30,
"msg": "User created successfully",
"service": "user-api",
"version": "1.2.3",
"environment": "production",
"userId": "123"
}
```
### Automatic Request Logging
When `logLevel: 'debug'`, the module automatically logs all incoming requests and responses:
```typescript
LoggingModule.forRoot({
logLevel: 'debug' // Enables automatic request/response logging
})
```
**Automatic logs include:**
- Request start: Method, URL, controller, handler
- Request completion: Response status, duration
- Request errors: Error details and stack traces (when `logErrors: true`)
**Example output:**
```json
{"level":20,"msg":"Processing an incoming request and dispatching it to a route handler.","controller":{"name":"UserController","path":"users"},"handler":{"name":"createUser","path":"/"},"req":{"method":"POST","url":"/users"}}
{"level":20,"msg":"Processed an incoming request that was successfully handled by a route handler.","controller":{"name":"UserController","path":"users"},"handler":{"name":"createUser","path":"/"},"res":{"statusCode":201},"duration":"45ms"}
```
### Error Logging
Control whether intercepted errors are automatically logged:
```typescript
LoggingModule.forRoot({
logLevel: 'debug', // Enable request logging
logErrors: true // Also log errors caught by interceptor
})
```
### Complete Configuration
```typescript
import { LoggingModule } from '@neoma/logging'
export class AppModule {}
```
## API Reference
### ApplicationLoggerService
Application-scoped logger for general application logging. Implements the NestJS `LoggerService` interface:
```typescript
class ApplicationLoggerService {
// Standard NestJS LoggerService methods
log(message: any, ...optionalParams: any[]): void
error(message: any, ...optionalParams: any[]): void
warn(message: any, ...optionalParams: any[]): void
debug?(message: any, ...optionalParams: any[]): void
verbose?(message: any, ...optionalParams: any[]): void
fatal?(message: any, ...optionalParams: any[]): void
}
```
**Usage:**
```typescript
export class UserService {
constructor(private logger: ApplicationLoggerService) {}
processUsers() {
this.logger.log('Processing batch of users')
// Logs: { msg: 'Processing batch of users', service: 'user-api', version: '1.0.0' }
}
}
```
### RequestLoggerService
Request-scoped logger that automatically includes HTTP request context. Extends `ApplicationLoggerService` with additional request details:
```typescript
export class UserController {
constructor(private logger: RequestLoggerService) {}
createUser( userData: any) {
this.logger.log('Creating user', { userId: userData.id })
// Logs: {
// msg: 'Creating user',
// userId: userData.id,
// service: 'user-api',
// version: '1.0.0',
// req: { method: 'POST', url: '/users', headers: {...} }
// }
}
}
```
**Key features:**
- **Request-scoped**: New instance per HTTP request
- **Automatic context**: Includes request method, URL, headers automatically
- **Request tracing**: Automatic request trace ID generation with ULID
- **Header extraction**: Extract correlation IDs from request headers
- **Context merging**: Combines `logContext` configuration with request details
- **Same interface**: Uses identical logging methods as `ApplicationLoggerService`
- **Middleware integration**: Automatically available as `req.logger` in routes/middleware
### Request Tracing
Every request automatically gets a unique trace ID included in all log entries. This enables request correlation across distributed systems:
```typescript
export class UserController {
constructor(private logger: RequestLoggerService) {}
createUser( userData: any) {
this.logger.log('Processing user creation')
this.logger.log('Validating user data')
this.logger.log('User created successfully')
// All logs will have the same requestTraceId:
// { requestTraceId: '01HKQJQM7R8N4X3Z2T1V5B6Y9C', msg: 'Processing user creation' }
// { requestTraceId: '01HKQJQM7R8N4X3Z2T1V5B6Y9C', msg: 'Validating user data' }
// { requestTraceId: '01HKQJQM7R8N4X3Z2T1V5B6Y9C', msg: 'User created successfully' }
}
}
```
#### Custom Correlation Headers
Extract trace IDs from incoming request headers (useful for microservice communication):
```typescript
LoggingModule.forRoot({
logRequestTraceIdHeader: 'x-correlation-id', // Case-insensitive header lookup
logContext: {
service: 'user-api'
}
})
```
**With header present:**
```bash
curl -H "x-correlation-id: abc123" POST /users
```
```json
{
"level": 30,
"msg": "Processing user creation",
"requestTraceId": "abc123",
"service": "user-api"
}
```
**With header missing:**
```bash
curl POST /users
```
```json
{
"level": 40,
"msg": "Request Trace Header 'x-correlation-id' not found, auto-generating trace ID: 01HKQJQM7R8N4X3Z2T1V5B6Y9C"
}
{
"level": 30,
"msg": "Processing user creation",
"requestTraceId": "01HKQJQM7R8N4X3Z2T1V5B6Y9C",
"service": "user-api"
}
```
**ULID Benefits:**
- **Sortable**: Lexicographically sortable by generation time
- **Compact**: 26 characters vs 36 for UUID
- **Random**: 80 bits of randomness for uniqueness
- **Readable**: Crockford Base32 encoding (no confusing characters)
### Middleware Integration
The module automatically attaches `RequestLoggerService` to all incoming requests as `req.logger`, making it available in middleware, guards, and route handlers:
```typescript
import { Request, Response, NextFunction } from 'express'
// In middleware
export function customMiddleware(req: Request, res: Response, next: NextFunction) {
req.logger.log('Custom middleware executed', { path: req.path })
next()
}
// In route handlers (alternative to injection)
export class UserController {
getUser( req: Request, id: string) {
req.logger.log('Fetching user', { userId: id })
// This logger includes request context automatically
}
}
### Usage Patterns
**Simple message:**
```typescript
logger.log('User logged in')
```
**Message with context object:**
```typescript
logger.log('User logged in', {
userId: '123',
email: 'user@example.com'
})
```
**Message with printf-style interpolation:**
```typescript
logger.log('Processing payment for user %s: %d %s', userId, amount, currency)
// Results in: { msg: 'Processing payment for user john: 100 USD' }
```
### LoggingConfiguration
```typescript
interface LoggingConfiguration {
/**
* Minimum log level to capture. Setting to 'debug' enables automatic request logging.
* @default 'log'
*/
logLevel?: 'verbose' | 'debug' | 'log' | 'warn' | 'error' | 'fatal'
/**
* Custom destination for log output (mainly for testing)
* @default process.stdout
*/
logDestination?: any
/**
* Fields to redact from logs for privacy/security
* @default []
*/
logRedact?: string[]
/**
* Global context to include with every log entry
* @default {}
*/
logContext?: any
/**
* Optional header name to extract trace ID from incoming requests
* Performs case-insensitive lookup and auto-generates ULID if missing
* @default null
*/
logRequestTraceIdHeader?: string
/**
* Whether to log errors caught by the RequestLoggerInterceptor
* @default false
*/
logErrors?: boolean
}
```
## Advanced Usage
### Testing Your Logs
Use ArrayStream for testing log output:
```typescript
import { Test } from '@nestjs/testing'
import { LoggingModule, ApplicationLoggerService } from '@neoma/logging'
import { ArrayStream } from '@neoma/logging/fixtures'
describe('MyService', () => {
let logger: ApplicationLoggerService
let logs: any[]
beforeEach(async () => {
logs = []
const module = await Test.createTestingModule({
imports: [
LoggingModule.forRoot({
logDestination: new ArrayStream(logs)
})
]
}).compile()
logger = module.get(ApplicationLoggerService)
})
it('should log user creation', () => {
logger.log('User created', { userId: '123' })
expect(logs).toContainEqual(
expect.objectContaining({
level: 30, // INFO level
msg: 'User created',
userId: '123'
})
)
})
})
```
## Performance
/logging is built on Pino, one of the fastest Node.js loggers:
- **Low overhead**: Minimal performance impact on your application
- **Asynchronous**: Non-blocking log operations
- **Efficient**: Optimized JSON serialization
- **Memory efficient**: Smart object redaction without deep cloning
## Comparison
| Feature | /logging | NestJS Built-in | nestjs-pino |
|---------|---------------|----------------|-------------|
| Performance | ⚡ High | 🐌 Low | ⚡ High |
| NestJS Integration | 🎯 Native | ✅ Built-in | 🔧 Manual |
| Request Scoping | ✅ Yes | ❌ No | ✅ Yes (AsyncLocalStorage) |
| Global Context | ✅ Built-in | ❌ No | 🔧 Manual |
| Field Redaction | ✅ Built-in | ❌ No | 🔧 Manual |
| Testing DX | ✅ Excellent | ❌ Poor | 🔧 Manual |
| TypeScript Support | ✅ Full | ✅ Full | ✅ Full |
## Features
- ✅ **ApplicationLoggerService** - Application-scoped logging with global context
- ✅ **RequestLoggerService** - Request-scoped logging with automatic request context
- ✅ **Automatic request logging** - Log all requests/responses when `logLevel: 'debug'`
- ✅ **Request tracing** - Automatic ULID generation for request correlation
- ✅ **Header extraction** - Extract trace IDs from request headers with fallback
- ✅ **Error interceptor** - Configurable automatic error logging
- ✅ **Middleware integration** - Logger available as `req.logger` on all requests
- ✅ **Field redaction** - Built-in sensitive data protection with Pino paths
- ✅ **Global context configuration** - Add application metadata to all logs
- ✅ **Memory buffer testing** - ArrayStream utility for comprehensive test coverage
- ✅ **Printf-style and object context** - Flexible logging patterns
## Coming Soon
- 🏷️ **Default log fields** - Automatic environment, version, and deployment metadata
- 📊 **Structured logging helpers** - Common log patterns and utilities
- 🔄 **Log rotation** - Built-in log file rotation
- 📈 **Metrics integration** - Automatic logging metrics collection
## Requirements
- Node.js >= 14
- NestJS >= 8
- TypeScript >= 4.5
## License
MIT License - see [LICENSE](./LICENSE) for details.
## Links
- [npm package](https://www.npmjs.com/package/@neoma/logging)
- [GitHub repository](https://github.com/shipdventures/neoma-logging)
- [Pino documentation](https://getpino.io/)
- [NestJS documentation](https://docs.nestjs.com/)