Reusable Node Code Modules, Part 9 - Logging
Effective Debugging: Implementing Reusable Logging Modules
Logging is essential for monitoring application behavior, diagnosing issues, and maintaining a history of application activities. Implementing a reusable logging module ensures consistent and efficient logging across projects. This guide covers how to structure reusable logging modules in Node.js using Express, manage logs efficiently, and integrate common libraries.
Common Libraries and Tools
1. Winston
Winston is a versatile and widely-used logging library for Node.js
Key Features
Transport Support: Supports multiple transport mechanisms (e.g., console, file, HTTP)
Log Levels: Customizable log levels for filtering logs
Formatters: Allows custom formatting of log messages
Asynchronous Logging: Efficient asynchronous logging
Integration: Easily integrates with other Node.js libraries
2. Bunyan
Bunyan is a fast and simple JSON logging library for Node.js.
Key Features
JSON Logging: Outputs logs in JSON format, making them easy to parse
Stream Support: Supports various log streams (e.g., console, file, HTTP)
Log Levels: Built-in support for log levels
Child Loggers: Allows creation of child loggers for structured logging
Performance: High performance with minimal overhead
3. Morgan
Morgan is a middleware for logging HTTP requests in Node.js
Key Features
HTTP Request Logging: Specifically designed for logging HTTP requests
Predefined Formats: Offers various predefined log formats
Customization: Allows custom log formats
Integration: Easily integrates with Express applications
Lightweight: Minimalistic and efficient
4. Pino
Pino is a low-overhead JSON logging library for Node.js
Key Features
Performance: Designed for high performance and low overhead
JSON Logging: Outputs logs in JSON format
Transport Support: Supports custom transport mechanisms
Log Levels: Supports various log levels
Integration: Easily integrates with other Node.js libraries
Comparison
Winston: Best for versatile logging with support for multiple transport mechanisms and custom formatting
Bunyan: Ideal for JSON logging with high performance and built-in support for log levels and child loggers
Morgan: Suitable for HTTP request logging with predefined and customizable formats
Pino: Best for high-performance logging with low overhead and JSON output
Examples and Wrapper Class
Example 1: Winston
Setup:
$ npm install winston
Configuration:
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'logs/app.log' })
]
});
const logMessage = (level, message) => {
logger.log({ level, message });
};
export default logMessage;
Wrapper Class:
class Logger {
constructor() {
this.logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'logs/app.log' })
]
});
}
log(level, message) {
this.logger.log({ level, message });
}
info(message) {
this.log('info', message);
}
error(message) {
this.log('error', message);
}
debug(message) {
this.log('debug', message);
}
}
export default new Logger();
Example 2: Bunyan
Setup:
$ npm install bunyan
Configuration:
import bunyan from 'bunyan';
const logger = bunyan.createLogger({ name: 'app', level: 'info' });
const logMessage = (level, message) => {
logger[level](message);
};
export default logMessage;
Wrapper Class:
class Logger {
constructor() {
this.logger = bunyan.createLogger({ name: 'app', level: 'info' });
}
log(level, message) {
this.logger[level](message);
}
info(message) {
this.log('info', message);
}
error(message) {
this.log('error', message);
}
debug(message) {
this.log('debug', message);
}
}
export default new Logger();
Example 3: Morgan
Setup:
$ npm install morgan
Configuration:
import morgan from 'morgan';
import fs from 'fs';
import path from 'path';
const logStream = fs.createWriteStream(path.join(__dirname, 'logs/access.log'), { flags: 'a' });
const requestLogger = morgan('combined', { stream: logStream });
export default requestLogger;
Usage in Express:
import express from 'express';
import requestLogger from './requestLogger';
const app = express();
app.use(requestLogger);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
Example 4: Pino
Setup:
$ npm install pino
Configuration:
import pino from 'pino';
const logger = pino({ level: 'info' });
const logMessage = (level, message) => {
logger[level](message);
};
export default logMessage;
Wrapper Class:
class Logger {
constructor() {
this.logger = pino({ level: 'info' });
}
log(level, message) {
this.logger[level](message);
}
info(message) {
this.log('info', message);
}
error(message) {
this.log('error', message);
}
debug(message) {
this.log('debug', message);
}
}
export default new Logger();