Shoulder.dev Logo Shoulder.dev

Handling Errors and Logging in benhall/express-demo

Scenario: In a web application built using benhall/express-demo, you may encounter unexpected errors during runtime. Properly handling these errors and logging them for debugging purposes is crucial for maintaining the application’s stability and performance. In this guide, we will walk you through setting up error handling and logging using middleware functions and configuration.

Solution:

  1. Error Handling:

First, let’s create an error handling middleware function. In your app.js file, add the following code:

const express = require('express');
const app = express();

app.use((err, req, res, next) => {
console.error(err.stack); // Log the error stack trace
res.status(500).send('Something went wrong.');
});

This middleware function logs the error stack trace to the console and sends a generic error message to the client.

  1. Logging:

To log errors and access logs, we will use the morgan library. Install it by running npm install morgan. In your app.js file, add the following code:

const morgan = require('morgan');

app.use(morgan('combined'));

This middleware function logs HTTP requests with their corresponding response data.

  1. Configuring Logging:

For more advanced logging, you can use a logging library like winston. Install it by running npm install winston. Create a new file called logger.js in the utils directory and add the following code:

const winston = require('winston');

const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File('logs/error.log', { level: 'error' })
]
});

module.exports = logger;

This code sets up a logger instance with a console output and an error log file.

Update your app.js file to use the logger:

const logger = require('./utils/logger');

app.use((err, req, res, next) => {
logger.error(err.message); // Log the error message
res.status(500).send('Something went wrong.');
});
  1. Setting up notifications for critical errors:

To set up notifications for critical errors, you can use a service like LogDNA or Sentry. Follow their respective documentation to set up error reporting and notifications.

Tests:

To verify the error handling and logging functionality, you can create a test file called error-test.js in the tests directory and add the following code:

const request = require('supertest')(app);
const assert = require('assert');

describe('Error Handling', () => {
it('should return a 500 status code for unexpected errors', (done) => {
request.get('/non-existent-route')
.expect(500)
.end((err, res) => {
assert.equal(res.text, 'Something went wrong.');
done();
});
});
});

describe('Logging', () => {
it('should log errors to the console and error.log file', (done) => {
const errorMessage = 'An unexpected error occurred';

request.get('/error')
.expect(500)
.end((err, res) => {
logger.info('Test log message');
assert.equal(err.message, errorMessage);
done();
});
});
});

This test file includes two tests: one for error handling and one for logging. The error handling test sends a request to a non-existent route and checks that the server returns a 500 status code and the generic error message. The logging test sends a request to a route that intentionally throws an error and checks that the error message is logged to both the console and the error.log file.