145 lines
5.0 KiB
JavaScript
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;
|
|
}
|
|
|