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
How Can You Transform Your JavaScript into a Speed Demon?

Turbocharging JavaScript: The Ultimate Guide to Blazing-Fast Web Apps

Blog Image
What Makes Node.js the Game-Changer for Modern Development?

JavaScript Revolutionizing Server-Side Development with Node.js

Blog Image
Is Webpack DevServer the Secret Sauce to Effortless Web Development?

Bridging the Chaos: How Webpack DevServer Keeps Your Web Development Zen

Blog Image
JavaScript Atomics and SharedArrayBuffer: Boost Your Code's Performance Now

JavaScript's Atomics and SharedArrayBuffer enable low-level concurrency. Atomics manage shared data access, preventing race conditions. SharedArrayBuffer allows multiple threads to access shared memory. These features boost performance in tasks like data processing and simulations. However, they require careful handling to avoid bugs. Security measures are needed when using SharedArrayBuffer due to potential vulnerabilities.

Blog Image
Unleash Node.js Streams: Boost Performance and Handle Big Data Like a Pro

Node.js streams efficiently handle large datasets by processing in chunks. They reduce memory usage, improve performance, and enable data transformation, compression, and network operations. Streams are versatile and composable for powerful data processing pipelines.

Blog Image
How Can You Use a Digital Shield to Make Your Website Hack-Proof?

Fortify Your Website's Defenses with CSP's Layered Security Strategy