Skip to main content

Morgan Logger | Tutorial on how to use in an Express application

· 9 min read
Sai Deepesh

Morgan is a popular HTTP logging library for Node.js. It is designed to be a simple and flexible tool for logging HTTP requests and responses in Node.js applications.

Cover Image

Using Morgan, you can easily log requests made to your Node.js server, including information such as the request method, the URL of the request, the status code of the response, and the length of the response body.

You can also customize the format of the log messages and specify which requests should be logged and which should be ignored.

Why should you use Morgan Logger?

Here are a few reasons why you should use Morgan:

  1. It is easy to use: Morgan Logger is a simple and lightweight logging library, making it easy to get started and integrate into your application.
  2. It is flexible: You can customize the format of the log messages and specify which requests should be logged and which should be ignored. This allows you to tailor the logging to your specific needs.
  3. It provides useful information: Morgan logs useful information about HTTP requests and responses, such as the request method, the URL, the status code, and the length of the response body. This can be helpful for debugging and understanding how your application is being used.
  4. It is widely used: Morgan is a popular logging library for Node.js and has a large user base, meaning it is well-tested and supported.

Types of Log Output Formats in Morgan Logger

Morgan logger provides an easy way to get started with logging. With its pre-defined logging formats, you can capture a lot of useful information. You can also write your customized logs using tokens.

Let us learn about these methods briefly.

Pre-defined Morgan Log formats

Morgan contains a few pre-defined output formats like:

  • combined
  • common
  • dev
  • short
  • tiny

morgan(’combined’):

It follows the standard apache combined output format while logging.

:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
Morgan logging combined format
Morgan logging combined format

morgan(’common’):

It follows the standard apache common output format while logging.

:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]
Morgan Logger common format
Morgan Logger common format

morgan('dev') :

It gives the concise output colored by response status for development use. The status token will be colored green for success codes, red for server error codes, yellow for client error codes, cyan for redirection codes, and uncolored for information codes.

:method :url :status :response-time ms - :res[content-length]
Morgan Logger dev format
Morgan Logger dev format

morgain('short') :

It is shorter than the default, also including response time.

Morgan Logger short format
Morgan Logger short format

morgan('tiny'):

It gives minimal output while logging the HTTP requests in the following format.

:method :url :status :res[content-length] - :response-time ms
Morgan Logger tiny format
Morgan Logger tiny format

Creating Tokens in Morgan Logger

In Morgan, tokens are functions that are identified by a colon (:) symbol. You can also create your own tokens using the .token() method provided by the Morgan library.

morgan.token('type', function (req, res) { return req.headers['content-type'] })

Here’s a small example of creating tokens:

const express = require('express')
const morgan = require('morgan')
const uuid = require('uuid')
const PORT = process.env.PORT || "5555";

morgan.token('id', (req) => { //creating id token
return req.id
})

const app = express()

app.use(assignId)
app.use(morgan(':id :method :url :response-time'))

app.get('/', function (req, res) {
res.send('hello, world!')
})

function assignId (req, res, next) {
const id = uuid.v4()
req.id = id
next()
}

app.listen(parseInt(PORT, 10), () => {
console.log(`Listening on http://localhost:${PORT}`);
});

Output:

Log Output
Log Output

Now let us Morgan Logger in a sample Nodejs application and send the captured logs to an open source log management tool, SigNoz.

Getting Started with Morgan Logger

Prerequisites

Steps to use Morgan Logger

Create a node project in the current directory:

mkdir morgan-nodejs-example
cd morgan-nodejs-example

Initialize an npm project:

npm init -y

Install express and morgan packages:

npm i morgan express

Create an entry file called index.js file:

touch index.js

Create a basic hello world express app:

const express = require("express");
const PORT = process.env.PORT || "5555";
const app = express();

app.use(express.json())

app.get("/", (req, res) => {
res.json({ method: req.method, message: "Hello World", ...req.body });
});

app.get('/404', (req, res) => {
res.sendStatus(404);
})

app.get("/user", (req, res, next) => {
try {
throw new Error("Invalid user");
} catch (error) {
res.status(500).send("Error!");
}
});

app.listen(parseInt(PORT, 10), () => {
console.log(`Listening on http://localhost:${PORT}`);
});

Run the server with the below command and hit http://localhost:5555:

node index.js

If done correctly, the console should show Listening on http://localhost:5555

Nodejs express app running on localhost
Nodejs express app running on localhost

At this point, the project structure should look like this:

/node_modules
/index.js
/logger.js
/package-lock.json
/package.json

Using the Morgan Logger

In the index.js file, include require morgan.

const morgan = require("morgan");

To use Morgan in your Express server, you can call an instance of it and pass it as an argument to the .use() middleware before handling any HTTP requests. Morgan has a set of predefined format strings called presets that you can use to create a logger middleware with a specific format and options. The tiny preset generates minimal output when logging HTTP requests.

app.use(morgan('tiny'));

The final code in your index.js file will look like this.

const express = require("express");
const morgan = require("morgan");
const PORT = process.env.PORT || "5555";
const app = express();

app.use(express.json())
app.use(morgan('tiny'))

app.get("/", (req, res) => {
res.json({ method: req.method, message: "Hello World", ...req.body });
});

app.get('/404', (req, res) => {
res.sendStatus(404);
})

app.get("/user", (req, res) => {
try {
throw new Error("Invalid user");
} catch (error) {
res.status(500).send("Error!");
}
});

app.listen(parseInt(PORT, 10), () => {
console.log(`Listening on http://localhost:${PORT}`);
});

Logs will be captured depending on the route we hit:

Logs generated using Morgan Logger
Logs generated using Morgan Logger

In a production environment, you will need a log management tool to store and manage your logs efficiently. In this tutorial, we will use SigNoz - an open source APM and observability tool for logs collected by Morgan logging library.

Sending logs to SigNoz deployed on Docker

We will dockerize our nodejs application and run the application in Docker. We will be using the console transport for Morgan. If SigNoz is running on the same host, it will automatically start collecting logs of all the docker containers.

Installing and running the SigNoz app

SigNoz can be installed on macOS or Linux computers in just three steps by using a simple install script.

The install script automatically installs Docker Engine on Linux. However, on macOS, you must manually install Docker Engine  before running the install script.

git clone -b main https://github.com/SigNoz/signoz.git
cd signoz/deploy/
./install.sh

Dockerising the Node app

Create a docker-compose.yaml file and paste the following code:

version: "3.9"

services:
app:
container_name: app
image: app
restart: always
build:
context: .
dockerfile: Dockerfile
target: base
ports:
- "${PORT}:${PORT}"

Create a Dockerfile (no file extension needed) and paste the following code:

FROM node:alpine as base

WORKDIR /morgan-nodejs-example # current project name

COPY package.json ./

RUN rm -rf node_modules && npm i

COPY . .

CMD ["node", "index.js"]

Before we can deploy our app on a Docker container, we need to set up the environment variable we will need to run the app. Create a file named .env in the root directory of your folder.

Since we defined the port as a variable in the docker-compose.yaml file, we need to set the port in the .env file:

PORT=5555

Running the app

Finally, we can deploy the Node app on a Docker container. To do so, use Docker Compose:

docker compose up --build

Once the build is successfully run, you should be able to see the following logs on the console.

Morgan Logger Docker Build
Morgan logger docker build

Observing the logs on SigNoz

Now, hit the different routes we’ve hit earlier to check the logs i.e /, /404, /user and we should be able to watch the logs in SigNoz as follows.

JSON logs in SigNoz
Logs captured using Morgan Logger on SigNoz

JSON logs in SigNoz
Logs captured using Morgan Logger on SigNoz

If SigNoz is installed on a different host, you can collect logs by following these instructions.

Conclusion

Logs are essential to a developer’s workflow and critical to debugging applications. Morgan is a simple logging library, making the process more flexible and extensible. It provides useful information and is easy to implement. Once the logs are generated, you can collect them with SigNoz.

SigNoz uses OpenTelemetry to collect logs. With OpenTelemetry, you can also correlate your logs with other telemetry signals like metrics and traces. Having contextual information in your logs can help you debug applications faster. You can get an overview of logs management in SigNoz from the logs documentation.

Related Posts

SigNoz - a lightweight open source ELK alternative

Winston Logger - Full tutorial with a sample Nodejs application