@develop-x/nest-logger v1.0.8
@develop-x/nest-logger
Overview
@develop-x/nest-logger
is a NestJS package that provides structured logging capabilities using Pino logger with OpenTelemetry integration. It offers high-performance logging with trace correlation and configurable output formats.
Installation
npm install @develop-x/nest-logger
Features
- High Performance: Built on Pino, one of the fastest Node.js loggers
- Structured Logging: JSON-formatted logs for better parsing and analysis
- OpenTelemetry Integration: Automatic trace ID correlation in logs
- Multiple Log Levels: Support for debug, info, warn, and error levels
- Child Loggers: Create contextual child loggers with additional metadata
- Pretty Printing: Human-readable output for development environments
- Configurable: Flexible configuration options for different environments
Usage
Module Import
Import the LoggerModule
in your application module:
import { Module } from '@nestjs/common';
import { LoggerModule } from '@develop-x/nest-logger';
@Module({
imports: [
LoggerModule.forRoot({
level: 'info',
serviceName: 'my-service',
}),
],
})
export class AppModule {}
Basic Usage
Inject the LoggerService
in your services or controllers:
import { Injectable } from '@nestjs/common';
import { LoggerService } from '@develop-x/nest-logger';
@Injectable()
export class UserService {
constructor(private readonly logger: LoggerService) {}
async createUser(userData: any) {
this.logger.info('Creating new user', { userId: userData.id });
try {
const user = await this.userRepository.save(userData);
this.logger.info('User created successfully', {
userId: user.id,
email: user.email
});
return user;
} catch (error) {
this.logger.error('Failed to create user', {
error: error.message,
userData,
stack: error.stack
});
throw error;
}
}
async getUserById(id: string) {
this.logger.debug('Fetching user by ID', { userId: id });
const user = await this.userRepository.findById(id);
if (!user) {
this.logger.warn('User not found', { userId: id });
return null;
}
return user;
}
}
API Reference
LoggerModule Configuration
forRoot(options: LoggerModuleOptions)
Configure the logger module with the following options:
interface LoggerModuleOptions {
level?: 'debug' | 'info' | 'warn' | 'error'; // Log level (default: 'info')
serviceName?: string; // Service name for logs
prettyPrint?: boolean; // Enable pretty printing (default: false)
destination?: string; // Log file destination (optional)
}
Example:
LoggerModule.forRoot({
level: 'debug',
serviceName: 'user-service',
prettyPrint: process.env.NODE_ENV === 'development',
})
LoggerService Methods
info(message: any, ...args: any[])
Log informational messages.
this.logger.info('User logged in', { userId: '123', timestamp: new Date() });
this.logger.info('Processing request', requestData, additionalContext);
warn(message: any, ...args: any[])
Log warning messages.
this.logger.warn('Rate limit approaching', {
userId: '123',
currentRequests: 95,
limit: 100
});
error(message: any, ...args: any[])
Log error messages.
this.logger.error('Database connection failed', {
error: error.message,
stack: error.stack,
connectionString: 'postgres://...'
});
debug(message: any, ...args: any[])
Log debug messages (only shown when log level is 'debug').
this.logger.debug('Cache hit', { key: 'user:123', ttl: 300 });
child(bindings: Record<string, any>)
Create a child logger with additional context.
const requestLogger = this.logger.child({
requestId: 'req-123',
userId: 'user-456'
});
requestLogger.info('Processing request'); // Will include requestId and userId
requestLogger.error('Request failed'); // Will include requestId and userId
Configuration Examples
Development Environment
LoggerModule.forRoot({
level: 'debug',
serviceName: 'my-service-dev',
prettyPrint: true, // Human-readable output
})
Production Environment
LoggerModule.forRoot({
level: 'info',
serviceName: 'my-service',
prettyPrint: false, // JSON output for log aggregation
})
File Logging
LoggerModule.forRoot({
level: 'info',
serviceName: 'my-service',
destination: './logs/app.log',
})
Advanced Usage
Request Context Logging
Create request-scoped loggers with correlation IDs:
import { Injectable, Scope } from '@nestjs/common';
import { LoggerService } from '@develop-x/nest-logger';
@Injectable({ scope: Scope.REQUEST })
export class RequestLoggerService {
private requestLogger: LoggerService;
constructor(private readonly logger: LoggerService) {}
setRequestContext(requestId: string, userId?: string) {
this.requestLogger = this.logger.child({
requestId,
userId,
});
}
info(message: string, data?: any) {
this.requestLogger.info(message, data);
}
error(message: string, data?: any) {
this.requestLogger.error(message, data);
}
}
Middleware Integration
Log HTTP requests and responses:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { LoggerService } from '@develop-x/nest-logger';
@Injectable()
export class LoggingMiddleware implements NestMiddleware {
constructor(private readonly logger: LoggerService) {}
use(req: Request, res: Response, next: NextFunction) {
const start = Date.now();
const requestId = req.headers['x-request-id'] || `req-${Date.now()}`;
const requestLogger = this.logger.child({ requestId });
requestLogger.info('Incoming request', {
method: req.method,
url: req.url,
userAgent: req.headers['user-agent'],
ip: req.ip,
});
res.on('finish', () => {
const duration = Date.now() - start;
requestLogger.info('Request completed', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration,
});
});
next();
}
}
Error Logging with Context
import { Injectable } from '@nestjs/common';
import { LoggerService } from '@develop-x/nest-logger';
@Injectable()
export class PaymentService {
constructor(private readonly logger: LoggerService) {}
async processPayment(paymentData: any) {
const paymentLogger = this.logger.child({
paymentId: paymentData.id,
userId: paymentData.userId,
amount: paymentData.amount,
});
paymentLogger.info('Starting payment processing');
try {
// Payment processing logic
const result = await this.paymentGateway.charge(paymentData);
paymentLogger.info('Payment processed successfully', {
transactionId: result.transactionId,
status: result.status,
});
return result;
} catch (error) {
paymentLogger.error('Payment processing failed', {
error: error.message,
errorCode: error.code,
stack: error.stack,
gatewayResponse: error.response?.data,
});
throw error;
}
}
}
Log Output Examples
Development (Pretty Print)
[2023-12-01 10:30:00.123] INFO (my-service/12345): User logged in
userId: "123"
timestamp: "2023-12-01T10:30:00.123Z"
traceId: "1234567890abcdef"
Production (JSON)
{
"level": 30,
"time": 1701423000123,
"pid": 12345,
"hostname": "server-01",
"serviceName": "my-service",
"msg": "User logged in",
"userId": "123",
"timestamp": "2023-12-01T10:30:00.123Z",
"traceId": "1234567890abcdef"
}
Integration with Observability
OpenTelemetry Trace Correlation
The logger automatically includes OpenTelemetry trace IDs when available:
// Traces and logs will be correlated automatically
this.logger.info('Processing order', { orderId: '123' });
// Output includes: "traceId": "1234567890abcdef"
Log Aggregation
The structured JSON output works well with log aggregation systems:
- ELK Stack: Elasticsearch, Logstash, Kibana
- Fluentd: Log collection and forwarding
- Grafana Loki: Log aggregation system
- Datadog: Application monitoring platform
Performance Considerations
Asynchronous Logging
Pino uses asynchronous logging by default for better performance:
// This won't block the event loop
this.logger.info('High-frequency log message', largeDataObject);
Log Level Filtering
Set appropriate log levels for production to reduce overhead:
// Only log info, warn, and error in production
LoggerModule.forRoot({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
})
Testing
Unit Testing with Logger
import { Test, TestingModule } from '@nestjs/testing';
import { LoggerService } from '@develop-x/nest-logger';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
let logger: LoggerService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: LoggerService,
useValue: {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
child: jest.fn().mockReturnThis(),
},
},
],
}).compile();
service = module.get<UserService>(UserService);
logger = module.get<LoggerService>(LoggerService);
});
it('should log user creation', async () => {
const userData = { id: '123', email: 'test@example.com' };
await service.createUser(userData);
expect(logger.info).toHaveBeenCalledWith(
'Creating new user',
{ userId: '123' }
);
});
});
Best Practices
- Structured Data: Always use objects for log data instead of string concatenation
- Consistent Fields: Use consistent field names across your application
- Sensitive Data: Never log sensitive information like passwords or tokens
- Performance: Use appropriate log levels to avoid performance impact
- Context: Include relevant context information in logs
- Child Loggers: Use child loggers for request-scoped or operation-scoped logging
Dependencies
@nestjs/common
: NestJS common utilities@nestjs/core
: NestJS core functionalitynestjs-pino
: NestJS integration for Pino loggerpino
: Fast JSON logger for Node.jspino-pretty
: Pretty printer for Pino logs@opentelemetry/api
: OpenTelemetry API for trace integration
License
ISC
Support
For issues and questions, please refer to the project repository or contact the development team.