traintimesPWA/server/lib/logging.js

173 lines
4.7 KiB
JavaScript

/**
* @fileOverview Utilities to simplify logging for the rest of the code.
*/
const winston = require('winston');
const _ = require('lodash');
const debug = require('debug')('logging:compliance');
/**
* Requiring `winston-mongodb` will expose
* `winston.transports.MongoDB`
*/
// eslint-disable-next-line import/no-unassigned-import
require('winston-mongodb');
module.exports = logging;
const MONGO_LOG_COLLECTION = 'ComplianceLog';
/**
* Initialise log transports
*/
const transports = [
new winston.transports.Console({
'name': 'console.info',
'format': winston.format.combine(
winston.format.timestamp(),
winston.format.simple()
),
'colorize': true,
'silent': false
})
];
let mongoTransport;
/**
* Create a very basic logger
*/
const logger = winston.createLogger({
'level': 'info',
transports
});
/**
* Trivially handle the 'error' event to prevent unhandled exception errors.
*/
logger.on('error', (error) => {
debug('Logger error:', error);
});
/**
* Initialisation functions
*/
module.exports.init = {
initMongoTransport
};
/**
* For test, also export a way to control the logger
*/
module.exports._test = {
'getLogger': () => logger,
'getTransports': () => transports
};
/**
* @typedef {function} BridgeLogFunction
* @param {Object} req - Express request object (to access IP, request ID, etc.)
* @param {string} [req.ip] - The IP address of the caller
* @param {string} [req.bridgeUniqueId] - The unique id of this request
* @param {string} [req.sessionData.User] - UserID for the user the request is for
* @param {string} msg - The basic message to log
* @param {Object} [opt] - Optional object containing additional values to log. These will be
* automatically prefixed with '_' and added to the logged object.
*/
/**
* @typedef {Object} BridgeLog
* @property {BridgeLogFunction} info - log at info level
* @property {BridgeLogFunction} error - log at error level
*/
/**
* Factory function for initialising logging for the specified calling file.
*
* It returns an object containing log functions such as `log.info()`, `log.error()`.
* The format of this functions is defined as @see BridgeLogFunction.
*
* @example
* const log = require('<path>/utils/logging.js')(__filename, 'utils:text:example');
* log.info(req, 'Some info text I want to log');
* log.error(req, 'Some error text I want to log', {customValue: 'Some custom value for this log'});
*
* @param {string} file - The filename. Usually just __filename
* @param {string} logID - A colon seperated id for this log group (e.g as used by debug())
* @returns {BridgeLog} - The logging object
*/
function logging(file, logID) {
return {
'info': doLog.bind(undefined, file, logID, 'info'),
'error': doLog.bind(undefined, file, logID, 'error')
};
}
/**
* Function to actually do the logging for any of the specific functions specified above.
*
* @param {string} file - the full filepath of the file that initialised this logger
* @param {string} logId - id of the log group
* @param {string} level - log level
* @param {Object} req - Express request object (to access IP, requestID etc.)
* @param {string} msg - The simple string to log
* @param {Object} opt - Additional options to log
*/
function doLog(file, logId, level, req, msg, opt) {
const toLog = {
level,
'message': msg,
'meta': {
logId,
'ip': req.ip,
'reqId': req.bridgeUniqueId,
'userId': _.get(req, 'session.data.user'),
file
}
};
const loggableOpt = _.mapKeys(opt, (value, key) => `_${ key}`);
//
// Update the base object with the loggable options (prefix with _ per GELF);
//
_.assign(toLog.meta, loggableOpt);
//
// Call the real logger and return the result
//
return logger.log(toLog);
}
/**
* The MongoDB `Db` class from the NodeJS driver
* @typedef {Object} Db
*/
/**
* Updates the logger to include a MongoDB transport that talks to the provided
* `db` instance.
* This also removes any prior MongoDB transport.
*
* @param {Db} db - the MongoDB `Db` instance to use for calling the DB.
*/
function initMongoTransport(db) {
// Remove any existing mongodb transport
if (mongoTransport)
logger.remove(mongoTransport);
//
// Create a new mongodb transport and register it with the logger
//
mongoTransport = new winston.transports.MongoDB({
'name': 'mongotransport',
'decolorize': true,
'storeHost': true,
db,
'collection': MONGO_LOG_COLLECTION,
'format': winston.format.combine(
winston.format.timestamp(),
winston.format.printf((info) => info.message)
)
});
logger.add(mongoTransport);
}