bridge-node-server/node_server/utils/init_morgan.js
Martin Donnelly 57bd6c8e6a init
2018-06-24 21:15:03 +01:00

145 lines
5.0 KiB
JavaScript

/**
* @fileOverview Helper utilities for initialising the morgan logging format
*/
'use strict';
const morgan = require('morgan');
const debug = require('debug')('logging:activity');
const Writeable = require('stream').Writable;
const mainDBP = require('../ComServe/mainDB-promises');
let initialised = false;
const MAX_BUFFER = 1000; // Max entries to buffer if the db is down
module.exports = {
init,
writeableStream
};
/**
* Initialises the morgan formats if it has not already been initialised
*/
function init() {
if (initialised) {
return;
}
//
// Define a morgan token to get the userId from the session
//
morgan.token('user-id', (req) => {
if (req.session && req.session.data) {
return req.session.data.user;
} else {
return '-';
}
});
//
// Define an Apache Combined Log equivalent format that uses our user id
// rather than default `basic-auth` user value. See:
// https://github.com/expressjs/morgan#user-content-combined
// for details of the base format.
//
morgan.format(
'bridge-combined',
':remote-addr - :user-id [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
);
initialised = true;
}
/**
* Function to create a new record for storing in the database.
*
* @param {string} record - the record value from Morgan
* @returns {Object} - an object suitable for storing in MongoDB
*/
function entry(record) {
return {
timestamp: new Date(),
request: record
};
}
/**
* Returns a new Writeable stream which can be used to log Morgan entries to
* the database via Morgan's `stream` parameter.
*
* @returns {Writeable} - A Writeable stream for use with Morgan logging
*/
function writeableStream() {
let buffer = [];
let writePending = false;
const writeable = new Writeable({
objectMode: true,
highWaterMark: 1,
write: function write(record, encoding, next) {
// Always log to stdout immediately
process.stdout.write(record + '\n');
if (writePending || !mainDBP.mainDB.dbOnline) {
// DB write in progress, or the DB is offline, so just buffer
if (buffer.length < MAX_BUFFER) {
buffer.push(entry(record));
debug('Buffered log message:', buffer.length, writePending);
} else {
process.stderr.write('Activity log buffer exceeded. MESSAGES WILL BE LOST!\n');
}
} else {
// Online so try to send entries to the db.
// There may be buffered entries, so swap them into pending array
// so more can buffer while we wait for the DB to confirm.
const pending = buffer.slice();
buffer = [];
// Add our new entry to the pending array
pending.push(entry(record));
// Try to upload them to mongo
debug('WRITE Started:', pending.length);
writePending = true;
mainDBP.addMany(mainDBP.mainDB.collectionActivityLog, pending, {}, false)
.then((result) => {
// The request ran, but may not have inserted everything.
// If it didn't we can't really know which ones were and
// were not inserted, so just notify the error.
if (result.result.ok) {
debug(' - WRITE OK:', result.result.n);
} else {
process.stderr.write('Some activity log entries may have failed to save to the db!\n');
debug(' - Write partial failure:', result.result.n);
}
writePending = false;
return result;
})
.catch((error) => {
debug(' - WRITE ERROR received:', error);
// The request didn't run for some reason; likely that the
// database went down. Add them back into the buffer,
// up to our max buffer size.
// Note: keep the original items as they are likely to be
// closer to the cause of the outage.
// Add any new entries to the back of our pending list
const temp = pending.concat(buffer);
// And copy up to MAX_BUFFER items back over to the buffer
buffer = temp.slice(0, MAX_BUFFER);
writePending = false;
return null; // We handled the error, so no need to pass on
});
}
//
// Allow the server to continue without waiting for the result of the write to db
//
next();
}
});
return writeable;
}