Logging Guide
This document describes the logging system used in the My Dashboard server application.
Overview
The server uses Winston as the logging library, providing structured logging with different log levels and environment-based configuration.
Log Levels
The application supports the following log levels (in order of severity):
- error (0) - Error messages for critical issues
- warn (1) - Warning messages for potential problems
- info (2) - Informational messages about application flow
- http (3) - HTTP request logs
- debug (4) - Detailed debug information (includes validation errors)
Configuration
Environment Variables
LOG_LEVEL
- Sets the minimum log level (default: based on NODE_ENV)NODE_ENV
- Determines default log level if LOG_LEVEL is not set
Default Log Levels by Environment
- production:
info
- Logs info, warn, and error messages - development:
debug
- Logs all messages including debug - test:
error
- Only logs error messages
Example Configuration
# .env file
LOG_LEVEL=debug
NODE_ENV=development
Usage
Basic Usage
Import the Logger utility:
import { Logger } from '../utils/logger';
Logging Methods
Error Logging
Use for critical errors that need immediate attention:
Logger.error('Database connection failed', {
error: err,
database: 'mysql'
});
Warning Logging
Use for potential issues that don't stop execution:
Logger.warn('API rate limit approaching', {
currentRate: 95,
limit: 100
});
Info Logging
Use for important application events:
Logger.info('Server started', {
port: 3000,
environment: 'production'
});
HTTP Logging
Use for HTTP request/response logging:
Logger.http('API request received', {
method: 'GET',
path: '/api/users'
});
Debug Logging
Use for detailed debugging information:
Logger.debug('Processing user data', {
userId: 123,
step: 'validation'
});
Note: Validation errors are automatically logged at the debug level by the error handler middleware. This is because validation errors are expected user input errors and don't require immediate attention in production logs.
Structured Logging
Always include relevant context as metadata:
// Good - includes context
Logger.error('Failed to fetch user', {
userId: 123,
error: err
});
// Bad - no context
Logger.error('Failed to fetch user');
Child Loggers
Create child loggers with default metadata for consistent context:
const serviceLogger = Logger.child({ service: 'UserService' });
serviceLogger.info('User created', { userId: 123 });
// Logs: { service: 'UserService', userId: 123, message: 'User created' }
Output Format
Console Output (Development)
Colorized, human-readable format:
2025-10-05 12:34:56 [info]: Server started { "port": 3000, "environment": "development" }
2025-10-05 12:34:57 [error]: Database error { "error": "Connection timeout" }
File Output (Production)
JSON format for easy parsing and analysis:
{
"timestamp": "2025-10-05T12:34:56.789Z",
"level": "error",
"message": "Database error",
"error": "Connection timeout"
}
Log Files (Production Only)
In production, logs are written to files in the logs/
directory:
logs/error.log
- Error level logs onlylogs/combined.log
- All log levels
Files are automatically rotated:
- Maximum file size: 5MB
- Maximum files kept: 5
Best Practices
1. Use Appropriate Log Levels
// Error - for failures that need attention
Logger.error('Payment processing failed', { orderId, error });
// Warn - for recoverable issues
Logger.warn('Cache miss, fetching from database', { key });
// Info - for important business events
Logger.info('User registered', { userId, email });
// Debug - for development/troubleshooting (includes validation errors)
Logger.debug('Cache hit', { key, ttl });
Note on Validation Errors: The error handler middleware automatically logs ValidationError
instances at the debug level. This keeps production logs clean while still providing visibility during development.
2. Include Relevant Context
// Good
Logger.error('API request failed', {
endpoint: '/api/users',
method: 'POST',
statusCode: 500,
error: err
});
// Bad
Logger.error('Request failed');
3. Don't Log Sensitive Information
// Bad - logs password
Logger.info('User login', { email, password });
// Good - no sensitive data
Logger.info('User login', { email });
4. Use Structured Data
// Good - structured metadata
Logger.info('Order processed', {
orderId: 123,
amount: 99.99,
currency: 'USD'
});
// Bad - string interpolation
Logger.info(`Order ${orderId} processed for $${amount}`);
5. Log Errors with Stack Traces
try {
await processOrder(orderId);
} catch (error) {
// Winston automatically includes stack traces for Error objects
Logger.error('Order processing failed', { orderId, error });
throw error;
}
Migration from console.log
If you're updating existing code, replace console methods with Logger:
// Before
console.log('Server started on port', port);
console.error('Error:', error);
console.warn('Warning:', message);
// After
Logger.info('Server started', { port });
Logger.error('Error occurred', { error });
Logger.warn('Warning', { message });
Testing
In tests, the Logger is automatically mocked to reduce noise. You can verify logging calls:
import { Logger } from '../utils/logger';
test('should log error on failure', async () => {
await failingFunction();
expect(Logger.error).toHaveBeenCalledWith(
'Operation failed',
expect.objectContaining({ error: expect.any(Error) })
);
});
Troubleshooting
Logs not appearing
- Check
LOG_LEVEL
environment variable - Verify
NODE_ENV
is set correctly - Ensure log level is appropriate for the message
Too many logs
- Increase
LOG_LEVEL
to reduce verbosity - In production, set
LOG_LEVEL=info
orLOG_LEVEL=warn
Log files not created
- Ensure the application has write permissions
- Check that
NODE_ENV=production
is set - Verify the
logs/
directory exists or can be created