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 includeclffor common long format (e.g.10/Oct/2000:13:55:36 +0000),isofor ISO 8601 format (e.g.2000–10–10T13:55:36.000),webfor 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 usereq.ip. Otherwise the standardreq.connection.remoteAddressvalue:remote-user— the user authenticated part for basic auth:req[header]— the givenheaderof the request. If it’s not present, it’ll be logged as-:res[header]— the givenheaderof the response. If it’s not present, it’ll be logged as-:response-time[digits]— the time between a request coming intomorganand 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 usereq.originalUrlif it exists otherwisereq.urlwill be used.:user-agent— the contents of theUser-Agentheader 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.