This document describes the logging standards and configuration for the TeachLink Backend application. The logging system provides structured, consistent logging across all modules with proper log levels, correlation IDs, and sensitive data protection.
-
AppLoggerService (
src/logging/logger.service.ts)- Winston-based logger service
- Implements NestJS LoggerService interface
- Provides structured JSON logging
- Supports file and console transports
-
Sensitive Data Masker (
src/logging/sensitive-data.masker.ts)- Automatically masks sensitive fields in log output
- Recursively masks nested objects and arrays
- Case-insensitive key matching
-
Correlation ID Utilities (
src/common/utils/correlation.utils.ts)- Generates and propagates correlation IDs across requests
- AsyncLocalStorage-based context propagation
- Supports both canonical and legacy header names
-
HTTP Logging Interceptor (
src/logging/http-logging.interceptor.ts)- Logs all HTTP requests and responses
- Includes correlation IDs, duration, and status codes
- Masks sensitive data in request bodies
-
Structured Logging (
src/logging/structured-logging.ts)- Console-based structured logging fallback
- Overrides console methods for consistent output
- Handles uncaught exceptions and rejections
| Variable | Description | Default | Required |
|---|---|---|---|
LOG_LEVEL |
Minimum log level to output | info |
No |
LOG_DIR |
Directory for log files | logs |
No |
LOG_TO_FILE |
Enable file logging | false (true in production) |
No |
SERVICE_NAME |
Service name for log identification | teachlink-backend |
No |
NODE_ENV |
Environment (production/development) | - | No |
The following log levels are supported, ordered from most to least severe:
- error - Error events that might still allow the application to continue running
- warn - Potentially harmful situations
- info - Informational messages that highlight the progress of the application
- debug - Fine-grained informational events that are most useful to debug an application
- verbose - Detailed information typically only of interest when diagnosing problems
All logs are output in structured JSON format:
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"service": "teachlink-backend",
"pid": 12345,
"correlationId": "cid-abc123-xyz789",
"message": "User logged in successfully",
"context": "AuthService",
"meta": {
"userId": "user_123",
"email": "user@example.com"
}
}When file logging is enabled, logs are rotated daily:
-
Application logs:
logs/app-YYYY-MM-DD.log- Max size: 20MB per file
- Retention: 14 days
-
Error logs:
logs/error-YYYY-MM-DD.log- Max size: 20MB per file
- Retention: 30 days
- Only contains error-level logs
import { Injectable, Logger } from '@nestjs/common';
import { AppLoggerService } from '../logging/logger.service';
@Injectable()
export class UserService {
constructor(private readonly logger: AppLoggerService) {}
createUser(userData: CreateUserDto) {
this.logger.log('Creating new user', 'UserService');
// ... business logic
this.logger.debug('User created successfully', 'UserService');
}
}// Info level - general informational messages
this.logger.log('User logged in', 'AuthService');
// Debug level - detailed debugging information
this.logger.debug('Processing payment with ID: payment_123', 'PaymentService');
// Warn level - potentially harmful situations
this.logger.warn('Rate limit approaching for user: user_123', 'RateLimitService');
// Error level - error events
this.logger.error('Failed to process payment', error.stack, 'PaymentService');
// Verbose level - very detailed information
this.logger.verbose('Cache hit for key: user_profile_123', 'CacheService');this.logger.log('User action completed', 'UserService');
// Metadata is automatically included via correlation ID and context
// For custom metadata, use the winston logger directly
this.logger.logRequest({
method: 'POST',
url: '/api/users',
userId: 'user_123',
action: 'create_user'
});
this.logger.logResponse({
statusCode: 201,
durationMs: 45,
userId: 'user_123'
});Correlation IDs are automatically propagated through the request context:
import { getCorrelationId, runWithCorrelationId } from '../common/utils/correlation.utils';
// Get current correlation ID
const correlationId = getCorrelationId();
// Run code with a specific correlation ID (useful for background jobs)
runWithCorrelationId(() => {
this.logger.log('Processing background job', 'JobService');
}, 'cid-job-123');The following field names are automatically masked in log output:
password,passwd,passsecrettoken,accesstoken,access_token,refreshtoken,refresh_tokenapikey,api_key,apitoken,api_tokenauthorization,authkey,privatekey,private_keyclientsecret,client_secretcredit_card,creditcard,cardnumber,card_numbercvv,cvcpinssn,socialsecuritynumber,social_security_number
- Masked values are replaced with
***MASKED*** - Masking is case-insensitive
- Nested objects and arrays are recursively masked
- Primitive values (strings, numbers, booleans) are passed through unchanged
const userData = {
email: 'user@example.com',
password: 'secret123',
profile: {
name: 'John Doe',
ssn: '123-45-6789'
}
};
// In logs, this becomes:
{
email: 'user@example.com',
password: '***MASKED***',
profile: {
name: 'John Doe',
ssn: '***MASKED***'
}
}All HTTP requests are automatically logged with the following information:
{
"event": "http_request",
"correlationId": "cid-abc123",
"method": "POST",
"url": "/api/users",
"headers": {
"content-type": "application/json",
"authorization": "***MASKED***"
},
"remoteAddress": "192.168.1.1",
"body": {
"email": "user@example.com",
"password": "***MASKED***"
}
}{
"event": "http_response",
"correlationId": "cid-abc123",
"method": "POST",
"url": "/api/users",
"statusCode": 201,
"durationMs": 45
}{
"event": "http_error",
"correlationId": "cid-abc123",
"method": "POST",
"url": "/api/users",
"statusCode": 500,
"durationMs": 120,
"error": {
"message": "Database connection failed",
"name": "DatabaseError"
}
}- Use appropriate log levels for each situation
- Include context information (service/module name)
- Use correlation IDs for tracing requests across services
- Log business-critical events (user actions, state changes)
- Log errors with stack traces for debugging
- Keep log messages concise and meaningful
- Log sensitive data (passwords, tokens, PII)
- Use console.log directly (use the logger service)
- Log at debug level in production (use info or warn)
- Log large objects (truncate or summarize)
- Log the same event multiple times
- Include stack traces for non-error logs
| Situation | Log Level | Example |
|---|---|---|
| Application startup/shutdown | info | "Application started on port 3000" |
| User actions | info | "User created account", "User logged in" |
| Business logic milestones | info | "Payment processed successfully" |
| Performance issues | warn | "Slow query detected: 2000ms" |
- Deprecated API usage | warn | "Using deprecated endpoint /api/v1/users" | | Configuration issues | warn | "Missing env var, using default" | | Application errors | error | "Failed to connect to database" |
- Validation failures | error | "Invalid user input" | | Detailed debugging | debug | "Cache miss for key: user_123" | | Function entry/exit | debug | "Entering method: processPayment" | | Very detailed tracing | verbose | "Processing item 1 of 100" |
Run logging service tests:
npm test -- src/logging/logger.service.spec.ts
npm test -- src/logging/sensitive-data.masker.spec.ts
npm test -- src/logging/http-logging.interceptor.spec.tsVerify logging integration:
npm run test:e2e- Check
LOG_LEVELenvironment variable - Verify
LOG_TO_FILEis set correctly for file logging - Ensure the logging module is imported in
AppModule
- Verify
CorrelationIdMiddlewareis registered inmain.ts - Check that
requestIdMiddlewareis applied - Ensure correlation headers are passed from upstream services
- Check if the field name is in the
SENSITIVE_KEYSset - Verify the masker is being called on all log output
- Add custom field names to the masker if needed
- Reduce log level from
debugtoinfoin production - Disable file logging if not needed
- Check log file rotation settings
- Avoid logging large objects
Before:
console.log('User created:', userData);
console.error('Error:', error);After:
this.logger.log('User created', 'UserService');
this.logger.error('Failed to create user', error.stack, 'UserService');Before:
import { Logger } from '@nestjs/common';
private logger = new Logger(UserService.name);
this.logger.log('User created');After:
import { AppLoggerService } from '../logging/logger.service';
constructor(private readonly logger: AppLoggerService) {}
this.logger.log('User created', 'UserService');