Logging HTTP Requests and Errors using Morgan.js


Morgan is a middleware that is available for Node.js which is used when we want to log HTTP requests. It is mainly used in Express projects.

Morgan helps us simplify the logging work of HTTP requests that come and sent from an application in a single statement. In normal situations, developers often write all the logging code by hand and then end up logging what to store, how to save, and where everything is saved.

It helps in gathering logs from your server and also prepares them for reading. It also has many predetermined defaults built-in to help the developers. It is suitable for both the big projects and the small projects.

Setting Up Morgan.js

Morgan can be installed with the help of NPM, like we normally do with any Node.js module −

npm install morgan

After the setup, you can use this module by telling Node.js to include Morgan in your app.

const morgan = require('morgan')

A Simple App Using Morgan

Now that we are familiar with the fundamentals, it's time we focus on creating a very simple app in which we will be logging with the help of Morgan. Consider the code shown below.

index.js

Example

const express = require('express')
const morgan = require('morgan')

const app = express()
const port = process.env.PORT || 8989

app.use(morgan('combined'))

app.get('/', function(req, res) {
   res.send('TutorialsPoint is Awesome!!!')
})
app.listen(port, () => {
   console.log(`Sample app listening at http://localhost:${8989}`)
})

In the above code we are demonstrating how easy it is to use Morgan when it comes to logging. As first, we imported the Morgan package and then we added it as a middleware and when we made a GET request to the "/" endpoint.

Output

When you run the code, it will show the following output on the terminal −

Sample app listening at http://localhost:8989

When you click on the link, it will show the following output on the browser −

TutorialsPoint is Awesome!!!

Then, it will print a log on the terminal, as shown below −

::1 - - [28/Sep/2022:05:20:44 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0
(Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/104.0.0.0 Safari/537.36"

Defining the Output Format of the Logs in Morgan

There are two ways in which we can define the output formats of the logs in Morgan. These are −

  • Predefined logs − This type of output formatting means that there are some predefined set of items that are available that you can log, and all you ever need is to choose the combination that suits your needs.

  • Manual Setup − This manual setup is done with the help of tokens.

Predefined Logs in Morgan

In total, there are five predefined formats that we can use when we want to quickly obtain information with the help of Morgan. These are −

  • combined − It is used when we want to set the logs to be of Apache standard combined format.

  • common − It simply refers to the common format Apache standard.

  • dev − It is nothing but a color-coded log format, and these color changes with the request status.

  • short − This is the shorter version, in which less items are printed.

  • tiny − even shorter version than the short format.

In case you want to use the "format" function in Morgan, you would require the tokens, req, and res.

  • In simple terms, the res is nothing but the HTTP request, and the res is HTTP response.

  • A token is nothing but an object that contains all the declared tokens.

If you use these three things, then the function will return a string that will be either a "longline" or "null", if you consider that you don't want to log everything.

Another predefined format that you can is −

app.use(morgan('tiny'))

A format string of predefined tokens will look something like this −

app.use(morgan(':method :url :res[content-length] - :response-time ms'))

It is also good to note that a predefined format string can produce the same result as a predefined token.

The output of the 'Tiny' format would look something like this −

GET / 201 - - 45.730 ms

Log Tokens

As mentioned earlier, we can even create our own custom tokens and logging format as an alternative to using the predefined formats. Morgan does have the complete access to the contents of an HTTP request and the response as well. In other words, it means that even if the application uses custom HTTP headers, Morgan will still be able to log them.

The idea is that you must generate your tokens if you wish to customize your middleware routines, and this can be done by calling the morgan.token() function with a name and a callback function which will be used to create a token.

One way to think about tokens is that they are nothing but basic placeholders that we can use in a middleware format string. It is true that the predefined formats effectively perform the same thing, though you can always match and mix any of the 13 tokens in order to get the exact logline you desire.

Morgan provides you with a token that looks exactly like the client's user agent, the requested URL, and the response time, among other things.

Now let's consider a case where we have an application that generates a custom HTTP header called "type-of-user", and we want to log the content of this header. We can do that with the code snippet shown below.

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

The above code generates a new custom token, and we can log it inside our code with the help of the Morgan log format, by adding ":type-of-user".

app.use(morgan(':method :url :status :type-of-user'));

It will log the following on the terminal −

Server listening on port :8989
GET / 201 user

Saving the Logs in a File

Sometimes it happens that we also want to redirect our logs to a particular file rather than simply just dumping them to the console or the application output in the terminal.

In Morgan, we can do the same by creating a new Stream object and then providing it to the middleware. Then, we will be able to route the logger's output to a single file. Consider the code snippet shown below.

let logStream = fs.createWriteStream(path.join(_dirname,'output.log'), {
   flags: 'a'
})
// setting up the logger
app.use(morgan('update', {
   stream: logStream
}))

This concept in which we are able to redirect the logs to an external file helps in increasing the flexibility of the developers.

index.js

The final code with multiple "predefined" and "logStream" of Morgan is shown below.

const express = require('express')
const morgan = require('morgan')
const Writable = require("stream").Writable

const app = express()
const port = process.env.PORT || 8989

let logStream = fs.createWriteStream(path.join(_dirname, 'output.log'), {
   flags: 'a'
})

// setting up the logger
app.use(morgan('update', {
   stream: logStream
}))

class MyStream extends Writable {
   write(line) {
      console.log("Logger - ", line)
   }
}

// Create a new format
morgan.token("timed", "A new :method request added for the :url. " + "In total, it took :total-time[2] milliseconds to be resolved")

let writer = new MyStream()

// Use the new format by name
app.use(morgan('timed', {
   stream: writer
}))

app.get('/', function(req, res) {
   res.send('TutorialsPoint is Awesome!')
})

app.listen(port, () => {
   console.log(`Application listening at http://localhost:${port}`)
})

Conclusion

In this tutorial, we explained how you can use the Morgan library to log text and use it as a middleware in your application. In addition, we discussed what types of predefined formats and custom formats are available and finally we explored how to use them with the help of a couple of examples.

Updated on: 11-Oct-2022

2K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements