Logging errors in an Express.js and EJS
Logging errors in an Express.js application is crucial for debugging and monitoring the health of your application. Proper error logging helps you identify and fix issues quickly. Here’s how you can implement error logging in an Express.js application using EJS for rendering error pages.
1. Setting Up Basic Error Logging
The simplest way to log errors is to use console.error()
in your error-handling middleware. This will log errors to the console, which is useful during development.
Example:
app.use((err, req, res, next) => {
console.error(err.stack); // Logs the error stack to the console
res.status(500).render('500', { error: err });
});
2. Using a Logging Library
For more advanced logging, you can use a logging library like Winston or Morgan. These libraries offer features like logging to files, setting log levels, and more.
Winston Example
Install Winston:
npm install winston
Configure Winston:
Create a logger configuration file, e.g.,
logger.js
:const winston = require('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: 'combined.log' }) ] }); module.exports = logger;
Use Winston in Error-Handling Middleware:
In your main application file:
const logger = require('./logger'); app.use((err, req, res, next) => { logger.error(err.stack); // Logs error stack using Winston res.status(500).render('500', { error: err }); });
Morgan Example
Morgan is primarily used for HTTP request logging but can be configured for error logging as well.
Install Morgan:
npm install morgan
Configure Morgan:
In your main application file:
const morgan = require('morgan'); const fs = require('fs'); const path = require('path'); // Create a write stream (in append mode) const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }); // Setup morgan to log requests to access.log app.use(morgan('combined', { stream: accessLogStream }));
To log errors specifically, you would still use Winston or another dedicated error logger, as Morgan is more suited for HTTP request logging.
3. Logging in Production vs. Development
In production, you might want to log errors to a file or an external logging service while keeping the console output clean. Here’s how to adjust logging for different environments:
const isProduction = process.env.NODE_ENV === 'production';
if (isProduction) {
// Log to file or external service in production
const fs = require('fs');
const path = require('path');
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' });
app.use(morgan('combined', { stream: accessLogStream }));
} else {
// Log to console in development
app.use(morgan('dev'));
}
app.use((err, req, res, next) => {
if (isProduction) {
// Log to a file or service
logger.error(err.stack);
} else {
// Log to console
console.error(err.stack);
}
res.status(500).render('500', { error: err });
});
4. Monitoring and Alerts
For more advanced monitoring and alerting, consider integrating with external monitoring services such as:
- Sentry: For error tracking and alerting.
- Loggly: For log aggregation and analysis.
- Datadog: For comprehensive monitoring and logging.
These services provide dashboards, search capabilities, and alerts that can help you monitor your application’s health and respond to issues quickly.
Summary
- Basic Logging: Use
console.error()
for simple error logging during development. - Advanced Logging: Use libraries like Winston or Morgan for more sophisticated logging needs.
- Environment-Specific Logging: Adjust logging strategies based on the environment (development vs. production).
- External Services: Integrate with monitoring and logging services for advanced features and alerts.