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 includeclf
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 usereq.ip
. Otherwise the standardreq.connection.remoteAddress
value:remote-user
— the user authenticated part for basic auth:req[header]
— the givenheader
of the request. If it’s not present, it’ll be logged as-
:res[header]
— the givenheader
of the response. If it’s not present, it’ll be logged as-
:response-time[digits]
— the time between a request coming intomorgan
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 usereq.originalUrl
if it exists otherwisereq.url
will be used.:user-agent
— the contents of theUser-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.