Menu Close

Easy Logging with the Morgan Express Middleware

Logging is an important part of any app. We want to know what activities are going on and look for information to fix problems when they arise.

In this article, we’ll look at how to use the morgan middleware for adding logging in Express apps.

Parameters

The morgan middleware lets us pass in 2 arguments. The first is the format and the second is the options object.

Format

The format is a string. For example, we can pass in 'tiny' to show minimal information.

Also, we can pass in a string with the fields we want to show lik1:

morgan(':method :url :status');

The format can also be a custom format function like the following:

const express = require('express');  
const bodyParser = require('body-parser');  
const morgan = require('morgan')  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
morgan((tokens, req, res) => {  
  return [  
    tokens.method(req, res),  
    tokens.url(req, res),  
    tokens.status(req, res),  
  ].join(' ')  
})
app.get('/', (req, res) => {  
  res.send('foo');  
});
app.listen(3000, () => console.log('server started'));

We get the same thing as:

morgan(':method :url :status');

with the function that’s passed into the morgan function’s returned value.

Options

morgan accepts several properties in the options object.

immediate

immediate logs on request instead of response. The data from the response won’t be logged.

skip

A function to determine when logging is skipped.

stream

Output stream for writing log lines, defaults to process.stdout .

Predefined Formats

combined

Standard Apache combined log output:

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

common

Standard Apache common log output:

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

dev

Logging for development use. The :status token will be colored red for server error code, yellow for client error codes, cyan for redirection codes and uncolored for other codes.

:method :url :status :response-time ms - :res[content-length]

short

Shorter than default and includes response time.

:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms

tiny

Minimal output:

:method :url :status :res[content-length] - :response-time ms

Tokens

We can create new tokens to log the fields we want.

For example, we can write:

const express = require('express');  
const bodyParser = require('body-parser');  
const morgan = require('morgan')  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
morgan.token('type', (req, res) => req.headers['content-type'])
app.get('/', (req, res) => {  
  res.send('foo');  
});
app.listen(3000);

Then :type will log req.headers[‘content-type’] .

Below are tokens built into morgan :

  • :date[format] — date with format in UTC. Formats available include clf for common long format (e.g. 10/Oct/2000:13:55:36 +0000 ), iso for ISO 8601 format (e.g. 2000–10–10T13:55:36.000 ), web for RFC 1123 format (e.g. Tue, 10 Oct 2000 13:55:36 GMT )
  • :http-version — HTTP version of the request
  • :method — HTTP method of the request
  • :referreer — Referrer header of the request. This will use the standard misspelled Referer header if it exists, otherwise will log Referrer
  • :remote-addr — the remote address of the request. This will use req.ip . Otherwise the standard req.connection.remoteAddress value
  • :remote-user — the user authenticated part for basic auth
  • :req[header] — the given header of the request. If it’s not present, it’ll be logged as -
  • :res[header] — the given header of the response. If it’s not present, it’ll be logged as -
  • :response-time[digits] — the time between a request coming into morgan and when the response headers are written in milliseconds.
  • :status — the response status. If the request/response cycle completes before a response was sent to the client, then the status will be empty
  • :url — the URL of the request. This will use req.originalUrl if it exists otherwise req.url will be used.
  • :user-agent — the contents of the User-Agent header of the request

morgan.compile(format)

This method compiles a format string into a format function for use by morgan .

A format string is a string that presents a single log line and can utilize token syntax.

Tokens are referred to by :token-name . If a token accepts arguments, we can pass it into [] .

Examples

Simple Example

Simple use of morgan is something like the following:

const express = require('express');  
const bodyParser = require('body-parser');  
const morgan = require('morgan')  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(morgan('combined'));
app.get('/', (req, res) => {  
  res.send('foo');  
});
app.listen(3000);

Then we get something like:

::ffff:172.18.0.1 - - [28/Dec/2019:01:04:22 +0000] "GET / HTTP/1.1" 304 - "https://repl.it/languages/express" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"

Writing Logs to a File

We can write logs to a file by setting thestream option of the option object and pass it into the second argument as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const morgan = require('morgan')  
const app = express();  
const fs = require('fs')  
const path = require('path')  
const appLogStream = fs.createWriteStream(path.join(__dirname, 'app.log'), { flags: 'a' })
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(morgan('combined', { stream: appLogStream}));
app.get('/', (req, res) => {  
  res.send('foo');  
});
app.listen(3000);

Then we get:

::ffff:172.18.0.1 - - [28/Dec/2019:01:06:44 +0000] "GET / HTTP/1.1" 304 - "https://repl.it/languages/express" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"

In app.log .

Rotating Logs

We can rotate between logs bu setting the interval property of the object.

For example, we can write:

const express = require('express');  
const bodyParser = require('body-parser');  
const morgan = require('morgan')  
const app = express();  
const fs = require('fs')  
const path = require('path')  
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'app.log'), { flags: 'a' })
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(morgan('combined', {  
  interval: '7d',  
  stream: accessLogStream  
}));
app.get('/', (req, res) => {  
  res.send('foo');  
});
app.listen(3000);

to rotate logs every week.

Custom Log Entry Format

We can define our own token and record it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const morgan = require('morgan')  
const app = express();  
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));  
app.use(morgan(':id :method :url :date[iso]'))
morgan.token('id', (req) => req.id);app.get('/', (req, res) => {  
  req.id = 1;  
  res.send('foo');  
});
app.listen(3000);

Then since we set req.id to 1, we get:

1 GET / 2019-12-28T01:11:07.646Z

1 is in the first column since we specified :id first in the format string before :method .

Conclusion

morgan is an easy to use logger for Express apps. It’s available as a middleware and we can specify our log format and data to log in each entry with tokens.

To log what we want that’s included in the preset tokens, we can define our own tokens using the token method.

Posted in Express, expressjs