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
Are Static Site Generators the Future of Web Development?

Transforming Web Development with Blazing Speed and Unmatched Security

Blog Image
Angular Elements: Build Reusable Components for Any Web App!

Angular Elements: Custom components as reusable web elements. Package Angular components for use in any web app. Ideal for gradual migration, micro frontends, and cross-framework reusability. Challenges include bundle size and browser support.

Blog Image
What Makes D3.js the Ultimate Magic Wand for Data Visualization?

Bringing Data to Life: Why D3.js Revolutionizes Web Visualization

Blog Image
Unlock Node.js Power: Clustering for Scalable, Multi-Core Performance Boost

Node.js clustering enables multi-core utilization, improving performance and scalability. It distributes workload across worker processes, handles failures, facilitates inter-process communication, and allows custom load balancing for efficient resource use.

Blog Image
Build a Real-Time Video Chat App in Angular with WebRTC!

WebRTC and Angular combine to create video chat apps. Key features include signaling server, peer connections, media streams, and screen sharing. Styling enhances user experience.

Blog Image
React's Secret Weapon: Lazy Loading for Lightning-Fast Apps

React.lazy and Suspense enable code-splitting, improving app performance by loading components on demand. This optimizes load times and enhances user experience, especially for large, complex applications.