Logging System
November 3, 2025 · View on GitHub
Table of Contents
Overview
This project includes a comprehensive logging system built on Morgan and Winston that provides structured logging, request tracing, and third-party API monitoring. The system supports both local development (console output) and production environments (AWS CloudWatch).
Key Features
- Structured JSON Logging - All logs are formatted as JSON for easy parsing
- Request Tracing - Automatic correlation ID generation and tracking
- Third-Party API Monitoring - Built-in logging for external API calls
- Sensitive Data Masking - Automatic sanitization of sensitive fields
- Environment-Aware - Different transports for local vs production
- Performance Tracking - Response time and duration logging
Architecture
The logging system follows a modular architecture with clear separation of concerns:
src/loggings/
├── loggings.service.ts # Core logging service
├── loggings.module.ts # Module definition
└── utils/
├── logger.utils.ts # HTTP request/response logging
├── correlation-id.middleware.ts # Correlation ID generation
├── request-context.interceptor.ts # Request context management
└── request-context.service.ts # Async context storage
Configuration
The logging system is configured through environment variables and automatically adapts based on the NODE_ENV setting.
Environment Variables
# Required
NODE_ENV=local|development|production
# AWS CloudWatch (production only)
AWS_LOGGING_GROUP=your-log-group
AWS_LOGGING_STREAM=your-log-stream
AWS_REGION=us-east-1
# Sensitive fields to mask
APP_MASKED_FIELDS=password,secret,token,key
Usage Examples
Basic Logging
import { LoggingsService } from '@src/loggings/loggings.service';
@Injectable()
export class UserService {
constructor(private readonly logger: LoggingsService) {}
async createUser(userData: CreateUserDto) {
this.logger.log('Creating new user', 'UserService');
try {
const user = await this.userRepository.save(userData);
this.logger.log(`User created with ID: ${user.id}`, 'UserService');
return user;
} catch (error) {
this.logger.error('Failed to create user', error.stack, 'UserService');
throw error;
}
}
}
HTTP Request/Response Logging
The system automatically logs all HTTP requests and responses. No additional code is required - it's configured in main.ts:
// In main.ts
const loggerService = app.select(LoggingsModule).get(LoggingsService);
configureLogger(app, loggerService);
Example Log Output:
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"message": "HTTP request/response",
"method": "POST",
"url": "/api/v1/users",
"status": 201,
"responseTime": 45,
"correlationId": "550e8400-e29b-41d4-a716-446655440000",
"userId": "123",
"clientIp": "192.168.1.100",
"requestBody": {"name": "John Doe", "email": "john@example.com"},
"responseBody": {"id": 1, "name": "John Doe", "email": "john@example.com"}
}
Third-Party API Logging
Use the LoggingHttpService for external API calls:
import { LoggingHttpService } from '@src/http/logging-http.service';
@Injectable()
export class PaymentService {
constructor(private readonly httpService: LoggingHttpService) {}
async processPayment(paymentData: PaymentDto) {
return this.httpService.request({
method: 'POST',
url: 'https://api.payment-provider.com/charges',
data: paymentData,
serviceName: 'PaymentProvider', // Optional: for better log categorization
}).toPromise();
}
}
Automatic Logging:
- Request start with payload and headers
- Response completion with status and duration
- Error handling with detailed error information
SDK Call Logging
For SDK-based integrations:
import { LoggingsService } from '@src/loggings/loggings.service';
@Injectable()
export class S3Service {
constructor(private readonly logger: LoggingsService) {}
async uploadFile(file: Buffer, key: string) {
const startTime = Date.now();
this.logger.logThirdPartySdkCallStart({
serviceName: 'AWS S3',
operation: 'uploadObject',
input: { key, size: file.length },
});
try {
const result = await this.s3Client.upload({
Bucket: 'my-bucket',
Key: key,
Body: file,
}).promise();
this.logger.logThirdPartySdkCallEnd({
serviceName: 'AWS S3',
operation: 'uploadObject',
durationMs: Date.now() - startTime,
output: { location: result.Location },
});
return result;
} catch (error) {
this.logger.logThirdPartySdkCallEnd({
serviceName: 'AWS S3',
operation: 'uploadObject',
durationMs: Date.now() - startTime,
error: { message: error.message, code: error.code },
});
throw error;
}
}
}
Environment Configuration
Local Development
In local environment, logs are output to console with colored formatting:
NODE_ENV=local
Console Output:
Time: [2024-01-15T10:30:00.000Z] → Level: info → Method: POST → URL: /api/v1/users → Message: HTTP request/response → Status: 201 → ResponseTime: (45ms) → CorrelationId: 550e8400-e29b-41d4-a716-446655440000 → ClientIp: 192.168.1.100 → UserId: 123
Production Environment
In production, logs are sent to AWS CloudWatch:
NODE_ENV=production
AWS_LOGGING_GROUP=my-app-logs
AWS_LOGGING_STREAM=api-logs
AWS_REGION=us-east-1
CloudWatch Log Format:
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"message": "HTTP request/response",
"method": "POST",
"url": "/api/v1/users",
"status": 201,
"responseTime": 45,
"correlationId": "550e8400-e29b-41d4-a716-446655440000",
"userId": "123",
"clientIp": "192.168.1.100",
"requestBody": {"name": "John Doe"},
"responseBody": {"id": 1, "name": "John Doe"}
}
Previous: Architecture
Next: HTTP Module