@neoma/logging
v0.4.0
Published
Great logging for NestJs
Downloads
416
Maintainers
Readme
@neoma/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 @neoma/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
npm install @neoma/logging pinoBasic Usage
Quick Start
Replace your existing NestJS logger with ApplicationLoggerService:
import { LoggingModule, ApplicationLoggerService } from '@neoma/logging'
import { Module } from '@nestjs/common'
@Module({
imports: [LoggingModule.forRoot()],
providers: [ApplicationLoggerService]
})
export class AppModule {}Using the Logger
import { Injectable } from '@nestjs/common'
import { ApplicationLoggerService } from '@neoma/logging'
@Injectable()
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:
LoggingModule.forRoot({
logLevel: 'warn' // Only log warnings, errors, and fatal messages
})Available levels (from most to least verbose):
verbose(maps to Pinotrace)debug- Enables automatic request/response logginglog(maps to Pinoinfo) - defaultwarnerrorfatal
Field Redaction
Protect sensitive data by configuring field redaction:
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:
logger.log('User login', {
username: 'john_doe',
password: 'secret123',
profile: {
email: '[email protected]',
apiKey: 'sk-1234567890'
}
})After redaction:
{
"level": 30,
"msg": "User login",
"username": "john_doe",
"password": "[REDACTED]",
"profile": {
"email": "[email protected]",
"apiKey": "[REDACTED]"
}
}Log Context
Add global context that gets included with every log entry:
LoggingModule.forRoot({
logContext: {
service: 'user-api',
version: '1.2.3',
environment: 'production'
}
})Result in logs:
{
"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:
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:
{"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:
LoggingModule.forRoot({
logLevel: 'debug', // Enable request logging
logErrors: true // Also log errors caught by interceptor
})Complete Configuration
import { LoggingModule } from '@neoma/logging'
@Module({
imports: [
LoggingModule.forRoot({
logLevel: 'debug',
logContext: {
service: 'user-api',
version: '1.0.0',
environment: process.env.NODE_ENV
},
logRedact: [
'password',
'*.secret',
'user.personalInfo.*',
'payment.cardNumber'
],
logRequestTraceIdHeader: 'x-correlation-id',
logErrors: true
})
]
})
export class AppModule {}API Reference
ApplicationLoggerService
Application-scoped logger for general application logging. Implements the NestJS LoggerService interface:
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:
@Injectable()
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:
@Injectable()
export class UserController {
constructor(private logger: RequestLoggerService) {}
@Post()
createUser(@Body() 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
logContextconfiguration with request details - Same interface: Uses identical logging methods as
ApplicationLoggerService - Middleware integration: Automatically available as
req.loggerin routes/middleware
Request Tracing
Every request automatically gets a unique trace ID included in all log entries. This enables request correlation across distributed systems:
@Controller('users')
export class UserController {
constructor(private logger: RequestLoggerService) {}
@Post()
createUser(@Body() 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):
LoggingModule.forRoot({
logRequestTraceIdHeader: 'x-correlation-id', // Case-insensitive header lookup
logContext: {
service: 'user-api'
}
})With header present:
curl -H "x-correlation-id: abc123" POST /users{
"level": 30,
"msg": "Processing user creation",
"requestTraceId": "abc123",
"service": "user-api"
}With header missing:
curl POST /users {
"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:
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)
@Controller('users')
export class UserController {
@Get(':id')
getUser(@Req() req: Request, @Param('id') 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:
logger.log('User logged in', {
userId: '123',
email: '[email protected]'
})Message with printf-style interpolation:
logger.log('Processing payment for user %s: %d %s', userId, amount, currency)
// Results in: { msg: 'Processing payment for user john: 100 USD' }LoggingConfiguration
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:
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
@neoma/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 | @neoma/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.loggeron 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 for details.
