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
Unlocking React Native's Magic: Build Faster, Smarter Apps with Ease

Ride the Wave of React Native's Revolutionary App-Building Magic With Speed, Simplicity, and Unmatched Craftsmanship at Your Fingertips

Blog Image
Unleash React's Power: Build Lightning-Fast PWAs That Work Offline and Send Notifications

React PWAs combine web and native app features. They load fast, work offline, and can be installed. Service workers enable caching and push notifications. Manifest files define app behavior. Code splitting improves performance.

Blog Image
Unlock Real-Time Magic: Build Collaborative Apps with React and Firebase

React and Firebase enable real-time collaborative apps. Users work together seamlessly, creating interactive experiences. Combine React's UI capabilities with Firebase's real-time database for powerful, engaging applications. Authentication and chat features enhance collaboration.

Blog Image
Styled Components: The Secret Weapon for Effortless React UI Magic

Styled Components in React: CSS-in-JS library for scoped, dynamic styles. Enables component-based styling, theming, and responsive design. Improves maintainability and encourages modular UI development.

Blog Image
Could a Progressive Web App Replace Your Favorite Mobile App?

Progressive Web Apps: Bridging the Gap Between Websites and Native Apps

Blog Image
8 Essential Asynchronous JavaScript Techniques for Efficient Web Development

Discover 8 essential asynchronous JavaScript techniques to build responsive web apps. Learn about callbacks, Promises, async/await, and more. Boost your coding skills now!