The Morgan package has two general areas for configuration:
- Log format
- Log location
As it stands, Notes uses the dev format, which is described as a concise status output meant for developers. This can be used to log web requests as a way to measure website activity and popularity. The Apache log format already has a large ecosystem of reporting tools and, sure enough, Morgan can produce log files in this format.
To change the format, simply change this line in app.js:
app.use(logger(process.env.REQUEST_LOG_FORMAT || 'dev'));
Then run Notes as follows:
$ REQUEST_LOG_FORMAT=common npm start
> notes@0.0.0 start /Users/david/chap07/notes
> node ./bin/www
::1 - - [12/Feb/2016:05:51:21 +0000] "GET / HTTP/1.1" 304 -
::1 - - [12/Feb/2016:05:51:21 +0000] "GET /vendor/bootstrap/css/bootstrap.min.css HTTP/1.1" 304 -
::1 - - [12/Feb/2016:05:51:21 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 -
::1 - - [12/Feb/2016:05:51:21 +0000] "GET /vendor/bootstrap/js/bootstrap.min.js HTTP/1.1" 304 -
To revert to the previous logging output, simply do not set this environment variable. If you've looked at Apache access logs, this logging format will look familiar. The ::1 notation at the beginning of the line is IPV6 notation for the localhost, which you may be more familiar with as 127.0.0.1.
We can declare victory on request logging and move on to debugging messages. However, let's look at logging this to a file directly. While it's possible to capture stdout through a separate process, Morgan is already installed in Notes and it does provide the capability to direct its output to a file.
The Morgan documentation suggests this:
// create a write stream (in append mode)
var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'})
// setup the logger
app.use(morgan('combined', {stream: accessLogStream}));
But this has a problem; it's impossible to perform log rotation without killing and restarting the server. Instead, we'll use their rotating-file-stream package.
First, install the package:
$ npm install rotating-file-stream --save
Then we add this code to app.js:
const fs = require('fs-extra');
...
const rfs = require('rotating-file-stream');
var logStream;
// Log to a file if requested
if (process.env.REQUEST_LOG_FILE) {
(async () => {
let logDirectory = path.dirname(process.env.REQUEST_LOG_FILE);
await fs.ensureDir(logDirectory);
logStream = rfs(process.env.REQUEST_LOG_FILE, {
size: '10M', // rotate every 10 MegaBytes written
interval: '1d', // rotate daily
compress: 'gzip' // compress rotated files
});
})().catch(err => { console.error(err); });
}
..
app.use(logger(process.env.REQUEST_LOG_FORMAT || 'dev', {
stream: logStream ? logStream : process.stdout
}));
Here, we're using an environment variable, REQUEST_LOG_FILE, to control whether to send the log to stdout or to a file. The log can go into a directory, and the code will automatically create that directory if it doesn't exist. By using rotating-file-stream (https://www.npmjs.com/package/rotating-file-stream), we're guaranteed to have log file rotation with no extra systems required.
The fs-extra module is being used because it adds Promise-based functions to the fs module (https://www.npmjs.com/package/fs-extra). In this case, fs.ensureDir checks if the named directory structure exists and, if not, the directory path is created.