Logging in Express JS using winston and morgan
Laurent Egbakou
Laurent Egbakou
February 23, 2022
2 min
Our work may have helped you ?
Support Us

Table Of Contents

Setting Up the Project
Configure Winston
Configure Morgan
Add the morgan middleware to the ExpressJS server
Run the app
Source code
Useful links


In this blog post, we are going to take a look at how to create a well-structured log system in Node JS using Express JS, Morgan, and Winston.

  • Morgan is a Node.js and Express middleware to log HTTP requests and errors.
  • Winston is a logger for just about everything. It includes support for multiple storage options and log levels, log queries, and even a built-in profiler.

I was looking for a robust logging system for a crypto dashboard API that I built one month ago and I found this article: https://levelup.gitconnected.com/better-logs-for-expressjs-using-winston-and-morgan-with-typescript-1c31c1ab9342 very useful. The sample made in the article is TypeScript based. So, if you're trying to use TypeScript instead of Js, you don't need to continue reading the current post, just check out the link. In my crypto project, I hadn't used TypeScript, that's why I am writing this blog post.


To follow along with this article, you will need:

Setting Up the Project

  • Create a new directory named express-morgan-winston for your project
  • Initialize a new Node project inside the directory using the npm init command
  • Install express, morgan, and winston as dependencies
  • Create your entry file (provided when using the npm init command: app.js in my case). This is where you will handle the main logic in your Express server
mkdir express-morgan-winston cd express-morgan-winston npm init npm install morgan winston --save touch src/app.js # or mdkdir src && echo "" > src/app.js

Configure Winston

Basically, we need to define :

  • Log severity levels in the development and production environments.
  • Different colors for each level (log messages will be printed in console and saved to files)
  • The type of transport: console, file, or both.

Create a file src/utils/logger.js and copy-paste the code below. All parts of the configuration are well explained.

const winston = require('winston'); // Define your severity levels. // With them, You can create log files, // see or hide levels based on the running ENV. const levels = { error: 0, warn: 1, info: 2, http: 3, debug: 4, } // This method set the current severity based on // the current NODE_ENV: show all the log levels // if the server was run in development mode; otherwise, // if it was run in production, show only warn and error messages. const level = () => { const env = process.env.NODE_ENV || 'development' const isDevelopment = env === 'development' return isDevelopment ? 'debug' : 'warn' } // Define different colors for each level. // Colors make the log message more visible, // adding the ability to focus or ignore messages. const colors = { error: 'red', warn: 'yellow', info: 'green', http: 'magenta', debug: 'white', } // Tell winston that you want to link the colors // defined above to the severity levels. winston.addColors(colors) // Chose the aspect of your log customizing the log format. const format = winston.format.combine( // Add the message timestamp with the preferred format winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), // Tell Winston that the logs must be colored winston.format.colorize({ all: true }), // Define the format of the message showing the timestamp, the level and the message winston.format.printf( (info) => `${info.timestamp} ${info.level}: ${info.message}`, ), ) // Define which transports the logger must use to print out messages. // In this example, we are using three different transports const transports = [ // Allow the use the console to print the messages new winston.transports.Console(), // Allow to print all the error level messages inside the error.log file new winston.transports.File({ filename: 'logs/error.log', level: 'error', }), // Allow to print all the error message inside the all.log file // (also the error log that are also printed inside the error.log( new winston.transports.File({ filename: 'logs/all.log' }), ] // Create the logger instance that has to be exported // and used to log messages. const logger = winston.createLogger({ level: level(), levels, format, transports, }) module.exports = logger

We have a logger utility file that is ready for use anywhere in our express app. Now let's configure Morgan.

Configure Morgan

The configuration we need to make here is very simple:

  • Define the logger we created as default instead of console.log
  • Define The message format that morgan middleware should use when it handles requests and tries to write logs.

So I created the file src/middlewares/morgan.middleware.js and add the following code:

const morgan = require("morgan"); const logger = require("../utils/logger"); const stream = { // Use the http severity write: (message) => logger.http(message), }; const skip = () => { const env = process.env.NODE_ENV || "development"; return env !== "development"; }; const morganMiddleware = morgan( // Define message format string (this is the default one). // The message format is made from tokens, and each token is // defined inside the Morgan library. // You can create your custom token to show what do you want from a request. ":remote-addr :method :url :status :res[content-length] - :response-time ms", // Options: in this case, I overwrote the stream and the skip logic. // See the methods above. { stream, skip } ); module.exports = morganMiddleware;

Add the morgan middleware to the ExpressJS server

const express = require("express"); const morganMiddleware = require("./middlewares/morgan.middleware"); // The morgan middleware does not need this. // This is for a manual log const logger = require("./utils/logger"); const app = express(); // Add the morgan middleware 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!" }); }); // Startup app.listen(3000, () => { logger.info('Server is running on port 3000'); });

Run the app

Run the app using the command node src/app.js and call the API status endpoint. Below is how my terminal looks like:

You can notice that a log file named logs/all.log was automatically generated and contains all log messages.

Source code

Our final project structure looks like below:

📘express-morgan-winston/ ┣ 📁logs/ ┃ ┣ ✨all.log ┃ ┗ ✨error.log ┣ 📁src/ ┃ ┣ 📁middlewares/ ┃ ┃ ┗ ✨morgan.middleware.js ┃ ┣ 📁utils/ ┃ ┃ ┗ ✨logger.js ┃ ┗ ✨app.js ┣ ✨package-lock.json ┗ ✨package.json

Check out the source code on my GitHub organization Lioncoding-oss.


If you find this blog post useful, please share it on your favorite social media. Don't forget to follow me on GitHub and Twitter. To send me a message, please use the contact form or DM me on Twitter.




Laurent Egbakou

Laurent Egbakou

Microsoft MVP | Founder

Microsoft MVP | Software Engineer ☁️ | Blogger | Open source contributor


Open Source

Social Media

Microsoft MVPAzure Developer Associate Badge

Related Posts

RSS Feed
Transform RSS Feed into API using Express JS
February 23, 2022
2 min
© 2024, All Rights Reserved.
Powered By @lioncoding

French Content 🔜

Quick Links

Advertise with usAbout UsContact UsHire Us

Social Media