javascript

How Can You Create a Diary for Your Node.js App with Morgan and Winston?

Express Logging Like a Pro: Mastering Morgan and Winston for Robust Node.js Applications

How Can You Create a Diary for Your Node.js App with Morgan and Winston?

Building a solid Node.js application using Express involves more than just writing a bunch of routes and middleware. One vital piece of the puzzle is logging. Proper logging is like having a diary for your app—it helps you debug, monitor, and maintain everything smoothly. In the world of Node.js, two popular logging tools you’ll hear a lot about are Morgan and Winston. Morgan specializes in logging HTTP requests, while Winston is more of an all-rounder, handling advanced logging tasks.

So, say you’re starting fresh with a new Node.js project. How would you go about setting things up with Morgan and Winston? Here’s how to do it.

First things first, you’ve got to set up your Node.js project with Express, Morgan, and Winston. Open your terminal and create a new directory for your project. Initialize it by running npm init and answer the prompts. Once that’s done, install Express, Morgan, and Winston by running:

npm install express morgan winston

Okay, with that part sorted, let’s move on to setting up Winston. Winston is like the Swiss army knife of logging libraries. This guy can log messages to multiple destinations (called transports), has custom levels, and even offers various formatting options.

Create a new file named logger.js in your project directory for configuring Winston:

const winston = require('winston');

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

module.exports = logger;

This config will have Winston log messages to the console, an all.log file, and an error.log file specifically for errors.

Next up, let’s set up Morgan. Morgan is straightforward and quite useful for logging HTTP requests. To mix it with Winston, you’ll need to configure it to write logs to Winston’s logger. Set up a morgan.middleware.js file:

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

const stream = {
  write: (message) => logger.http(message.trim()),
};

const skip = () => {
  const env = process.env.NODE_ENV || 'development';
  return env !== 'development';
};

const morganMiddleware = morgan(':method :url :status :res[content-length] - :response-time ms', { stream, skip });

module.exports = morganMiddleware;

In this script, Morgan writes HTTP request logs to Winston’s logger, categorized at the http severity level.

Now, let’s wire everything up within your Express application. Create an app.js file and set up your Express server:

const express = require('express');
const morganMiddleware = require('./middlewares/morgan.middleware');
const logger = require('./utils/logger');

const app = express();

app.use(morganMiddleware);

app.get('/api/status', (req, res) => {
  logger.info('Checking the API status: Everything is OK');
  res.status(200).send({ status: 'UP', message: 'The API is up and running!' });
});

app.listen(3000, () => {
  logger.info('Server is running on port 3000');
});

This setup has the morganMiddleware attached to the Express app to log incoming requests. The logger is used to log custom messages inside your application.

You can bring your application to life by running this command in your terminal:

node src/app.js

When you access the API status endpoint, you’ll notice the logs getting generated both in the console and in the log files (all.log and error.log).

Now, let’s dive into a bit of customization. Morgan lets you adjust the log format with tokens. This format logs the remote IP address, request method, URL, response status, response content length, and response time:

const morganMiddleware = morgan(':remote-addr :method :url :status :res[content-length] - :response-time ms', { stream, skip });

Got an external API you talk to? You can also log calls to external services manually, as Morgan only logs incoming requests to your server. Here’s an example that uses Axios for making external API calls:

const axios = require('axios');

app.get('/external-api', async (req, res) => {
  try {
    const response = await axios.get('https://api.example.com/data');
    logger.info(`Successful call to external API: ${response.status} ${response.statusText}`);
    res.json(response.data);
  } catch (err) {
    logger.error(`Error calling external API: ${err.message}`);
    res.status(500).send('Internal server error');
  }
});

In this scenario, we manually log whether the call was successful or if it failed using Winston.

Winston also supports advanced features such as multiple transports and custom levels. Here’s how you can set up multiple loggers with different transports:

const serviceALogger = winston.loggers.get('serviceALogger');
const serviceBLogger = winston.loggers.get('serviceBLogger');

serviceALogger.error('Logging to a file');
serviceBLogger.warn('Logging to the console');

This setup allows you to direct log messages from various parts of your app to different places. Handy, right?

Logging isn’t just for debugging; it’s also valuable for monitoring and analysis. By keeping track of your HTTP requests and other key events, you can collect valuable data on your application’s performance and usage patterns. Tools like Elasticsearch and Kibana can help you visualize and analyze these logs, providing insights into how your app behaves.

So, in a nutshell, combining Morgan and Winston gives you a robust logging setup for your Node.js applications. Morgan takes care of logging HTTP requests, while Winston handles the heavy lifting with advanced logging needs like writing to files and using custom transports. Integrate these tools into your Express application, and you’ll ensure that your logs are not only useful but also well-organized and insightful. Happy coding!

Keywords: 1. Node.js 2. Express 3. Morgan 4. Winston 5. logging 6. HTTP requests 7. middleware 8. debugging 9. monitoring 10. log files



Similar Posts
Blog Image
Why Should Serving Directory Listings Be a Headache with Express.js Magic?

Effortlessly Navigate Your Project with Express.js and Serve-Index Magic

Blog Image
Is Vue.js The Secret Sauce to Your Next Web Project?

Unleash Your Web Creativity with the Progressive Powerhouse of Vue.js

Blog Image
Serverless Architecture with Node.js: Deploying to AWS Lambda and Azure Functions

Serverless architecture simplifies infrastructure management, allowing developers to focus on code. AWS Lambda and Azure Functions offer scalable, cost-effective solutions for Node.js developers, enabling event-driven applications with automatic scaling and pay-per-use pricing.

Blog Image
Dynamic Imports in Jest: Strategies for Testing Code Splitting

Dynamic imports optimize web apps by loading code on-demand. Jest testing requires mocking, error handling, and integration tests. Strategies include wrapper functions, manual mocks, and simulating user interactions for comprehensive coverage.

Blog Image
Temporal API: JavaScript's Time-Saving Revolution for Effortless Date Handling

The Temporal API is a proposed replacement for JavaScript's Date object, offering improved timezone handling, intuitive time arithmetic, and support for various calendar systems. It introduces new object types like PlainDate, ZonedDateTime, and Duration, making complex date calculations and recurring events easier. With better DST handling and exact time arithmetic, Temporal promises cleaner, more reliable code for modern web development.

Blog Image
5 Essential TypeScript Utility Types That Transform JavaScript Development

Discover the 5 essential TypeScript utility types that simplify complex type transformations and boost code quality. Learn how Partial, Pick, Omit, Record, and ReturnType can transform your development workflow and reduce runtime errors. #TypeScript #WebDev