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
Master Angular Universal: Boost SEO with Server-Side Rendering and SSG!

Angular Universal enhances SEO for SPAs through server-side rendering and static site generation. It improves search engine indexing, perceived performance, and user experience while maintaining SPA interactivity.

Blog Image
JavaScript's Time Revolution: Temporal API Simplifies Date Handling and Boosts Accuracy

The Temporal API is a new JavaScript feature that simplifies date and time handling. It introduces intuitive types like PlainDateTime and ZonedDateTime, making it easier to work with dates, times, and time zones. The API also supports different calendar systems and provides better error handling. Overall, Temporal aims to make date-time operations in JavaScript more reliable and user-friendly.

Blog Image
Mastering the Art of In-App Payments: A Journey Through React Native's Maze

Sailing Through In-App Payment Adventures with Apple and Google Pay: A React Native Developer's Treasure Map

Blog Image
Jest and Webpack: Optimizing for Lightning-Fast Test Runs

Jest and Webpack optimize JavaScript testing. Parallelize Jest, mock dependencies, use DllPlugin for Webpack. Organize tests smartly, use cache-loader. Upgrade hardware for large projects. Fast tests improve code quality and developer happiness.

Blog Image
Why Should JavaScript Developers Fall in Love with Jasmine?

Jasmine: The Secret Sauce for Smooth JavaScript Testing Adventures

Blog Image
Testing Custom Hooks in React: Jest Techniques You Didn’t Know About

Testing custom React hooks: Use renderHook, mock dependencies, control time with Jest timers, simulate context, handle Redux, and test complex scenarios. Ensure reliability through comprehensive testing.