Skip to content
Plurafy

Logging Levels and Strategies

Proper logging strategies are essential for debugging and monitoring applications. Learn to implement tiered logging, structured logs, and effective log management.

Implement a comprehensive logging strategy that captures appropriate information at each level while maintaining performance and security.

  • ERROR: System errors requiring attention
  • WARN: Potentially problematic situations
  • INFO: General informational messages
  • DEBUG: Detailed diagnostic information
  • TRACE: Very detailed debugging information
  • Centralized Logging: Aggregate logs from all services
  • Structured Logging: Machine-parseable log format
  • Contextual Logging: Include request context
  • Asynchronous Logging: Non-blocking log operations
const fs = require('fs');
const path = require('path');
class Logger {
constructor(options = {}) {
this.level = options.level || 'info';
this.logFile = options.logFile || 'app.log';
this.maxSize = options.maxSize || 10 * 1024 * 1024; // 10MB
this.maxFiles = options.maxFiles || 5;
this.levels = {
error: 0,
warn: 1,
info: 2,
debug: 3,
trace: 4,
};
}
shouldLog(level) {
return this.levels[level] <= this.levels[this.level];
}
format(level, message, meta) {
const timestamp = new Date().toISOString();
return JSON.stringify({
timestamp,
level,
message,
...meta,
});
}
write(entry) {
// Console output
console.log(entry);
// File output with rotation
this.writeToFile(entry);
}
writeToFile(entry) {
try {
const stats = fs.statSync(this.logFile);
if (stats.size > this.maxSize) {
this.rotateFile();
}
} catch (e) {
// File doesn't exist yet
}
fs.appendFileSync(this.logFile, entry + '\n');
}
rotateFile() {
const ext = path.extname(this.logFile);
const name = path.basename(this.logFile, ext);
const dir = path.dirname(this.logFile);
const rotated = path.join(dir, `${name}.${Date.now()}${ext}`);
fs.renameSync(this.logFile, rotated);
}
error(message, meta = {}) {
if (this.shouldLog('error')) {
this.write(this.format('ERROR', message, meta));
}
}
warn(message, meta = {}) {
if (this.shouldLog('warn')) {
this.write(this.format('WARN', message, meta));
}
}
info(message, meta = {}) {
if (this.shouldLog('info')) {
this.write(this.format('INFO', message, meta));
}
}
debug(message, meta = {}) {
if (this.shouldLog('debug')) {
this.write(this.format('DEBUG', message, meta));
}
}
trace(message, meta = {}) {
if (this.shouldLog('trace')) {
this.write(this.format('TRACE', message, meta));
}
}
}
module.exports = new Logger({ level: 'info' });
const uuid = require('uuid');
class RequestLogger {
constructor(logger) {
this.logger = logger;
}
middleware() {
return (req, res, next) => {
// Generate unique request ID
req.id = req.headers['x-request-id'] || uuid.v4();
// Create context for this request
req.logger = this.createContextLogger(req.id);
// Log request
req.logger.info('Incoming request', {
method: req.method,
path: req.path,
query: req.query,
ip: req.ip,
});
// Log response
const originalJson = res.json;
res.json = function(data) {
req.logger.info('Response sent', {
statusCode: res.statusCode,
responseTime: Date.now() - req.startTime,
});
return originalJson.call(this, data);
};
req.startTime = Date.now();
next();
};
}
createContextLogger(requestId) {
return {
info: (msg, meta) => this.logger.info(msg, { requestId, ...meta }),
error: (msg, meta) => this.logger.error(msg, { requestId, ...meta }),
warn: (msg, meta) => this.logger.warn(msg, { requestId, ...meta }),
debug: (msg, meta) => this.logger.debug(msg, { requestId, ...meta }),
};
}
}
// Usage
const requestLogger = new RequestLogger(logger);
app.use(requestLogger.middleware());
const logger = {
development: {
level: 'debug',
format: 'pretty',
transports: ['console', 'file'],
},
production: {
level: 'info',
format: 'json',
transports: ['file', 'sentry'],
redactFields: ['password', 'token', 'apiKey'],
},
test: {
level: 'error',
format: 'json',
transports: ['memory'],
silent: process.env.CI === 'true',
},
};
function getLogConfig() {
const env = process.env.NODE_ENV || 'development';
return logger[env] || logger.development;
}
// Redact sensitive information
function redactSensitive(data, redactFields) {
const redacted = { ...data };
redactFields.forEach(field => {
if (redacted[field]) {
redacted[field] = '***REDACTED***';
}
});
return redacted;
}
class PerformanceLogger {
constructor(logger, threshold = 1000) {
this.logger = logger;
this.threshold = threshold; // milliseconds
}
middleware() {
return (req, res, next) => {
const startTime = process.hrtime.bigint();
res.on('finish', () => {
const endTime = process.hrtime.bigint();
const duration = Number(endTime - startTime) / 1000000; // Convert to ms
if (duration > this.threshold) {
this.logger.warn('Slow endpoint', {
method: req.method,
path: req.path,
duration: `${duration.toFixed(2)}ms`,
threshold: `${this.threshold}ms`,
});
} else {
this.logger.debug('Endpoint performance', {
method: req.method,
path: req.path,
duration: `${duration.toFixed(2)}ms`,
});
}
});
next();
};
}
measureAsync(name, fn) {
return async (...args) => {
const start = process.hrtime.bigint();
try {
const result = await fn(...args);
const duration = Number(process.hrtime.bigint() - start) / 1000000;
this.logger.debug(`${name} executed`, { duration: `${duration.toFixed(2)}ms` });
return result;
} catch (error) {
const duration = Number(process.hrtime.bigint() - start) / 1000000;
this.logger.error(`${name} failed`, {
duration: `${duration.toFixed(2)}ms`,
error: error.message,
});
throw error;
}
};
}
}
// Usage
const perfLogger = new PerformanceLogger(logger, 500);
app.use(perfLogger.middleware());
const measuredDatabaseQuery = perfLogger.measureAsync(
'User.findById',
(userId) => database.users.findById(userId)
);
class ErrorLogger {
constructor(logger) {
this.logger = logger;
}
logError(error, context = {}) {
const errorInfo = {
message: error.message,
type: error.constructor.name,
stack: error.stack,
code: error.code,
statusCode: error.statusCode || 500,
...context,
};
this.logger.error('Error occurred', errorInfo);
// Additional error tracking
if (error.statusCode >= 500) {
// Critical error - notify team
this.notifyTeam(errorInfo);
}
}
middleware() {
return (err, req, res, next) => {
this.logError(err, {
requestId: req.id,
method: req.method,
path: req.path,
userAgent: req.get('user-agent'),
ip: req.ip,
});
const statusCode = err.statusCode || 500;
res.status(statusCode).json({
error: {
message: err.message,
code: err.code,
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
},
});
};
}
notifyTeam(errorInfo) {
// Send to Slack, email, etc.
console.log('Notifying team about critical error...');
}
}
// Usage
app.use((err, req, res, next) => {
new ErrorLogger(logger).middleware()(err, req, res, next);
});
const winston = require('winston');
const WinstonCloudWatch = require('winston-cloudwatch');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console({
format: winston.format.simple(),
}),
new WinstonCloudWatch({
awsRegion: 'us-east-1',
logGroupName: `/app/${process.env.NODE_ENV}`,
logStreamName: `${process.env.HOSTNAME}-${Date.now()}`,
messageFormatter: ({ level, message, meta }) =>
`[${level}] ${message} ${JSON.stringify(meta)}`,
}),
],
exceptionHandlers: [
new winston.transports.File({ filename: 'exceptions.log' }),
],
});
module.exports = logger;
  1. Appropriate Log Levels

    • Use ERROR for system failures
    • Use WARN for performance issues
    • Use INFO for important events
    • Use DEBUG during development
  2. Structured Logging

    • Always use JSON format
    • Include context information
    • Add timestamps and IDs
    • Maintain consistent schema
  3. Performance

    • Don’t log synchronously
    • Use log levels to filter verbosity
    • Rotate log files regularly
    • Archive old logs
  4. Security

    • Never log passwords or tokens
    • Redact PII
    • Control log access
    • Secure log storage
  5. Maintenance

    • Document logging standards
    • Review log queries regularly
    • Monitor log storage
    • Set retention policies
  • Production Monitoring: Track application health
  • Debugging: Find issues quickly
  • Auditing: Track user actions
  • Performance Analysis: Identify bottlenecks
  • Compliance: Maintain audit trails