bunyan-middleware
Version:
Request, response logger middleware using bunyan. Also provides request<>response duration.
130 lines (108 loc) • 3.55 kB
JavaScript
var uuid = require('uuid').v1
module.exports = function (options, logger) {
options = options || {}
logger = logger || options.logger
if (!logger && options.constructor && options.constructor.name === 'Logger') {
logger = options
options = {}
}
if (!logger) {
throw new Error('`logger` is required')
}
var headerName = options.headerName || 'X-Request-Id'
, headerNameLower = headerName.toLowerCase()
, propertyName = options.propertyName || 'reqId'
, additionalRequestFinishData = options.additionalRequestFinishData
, logName = options.logName || 'req_id'
, obscureHeaders = options.obscureHeaders
, excludeHeaders = options.excludeHeaders
, requestStart = options.requestStart || false
, verbose = options.verbose || false
, filter = options.filter
, parentRequestSerializer = logger.serializers && logger.serializers.req
, level = options.level || 'info'
function processHeaderNames(property) {
if (property && property.length) {
return property.map(function(name) { return name.toLowerCase() })
} else {
return false
}
}
obscureHeaders = processHeaderNames(obscureHeaders)
excludeHeaders = processHeaderNames(excludeHeaders)
function requestSerializer(req) {
var obj
if (parentRequestSerializer) {
obj = parentRequestSerializer(req)
} else {
obj =
{ method: req.method
, url: req.originalUrl || req.url
, headers: req.headers
, query: req.query
, remoteAddress: req.connection.remoteAddress
, remotePort: req.connection.remotePort
}
}
if (obj.headers && (obscureHeaders || excludeHeaders)) {
obj.headers = Object.keys(obj.headers).reduce(function(memo, name) {
if (excludeHeaders && excludeHeaders.includes(name)) {
return memo
}
if (obscureHeaders && obscureHeaders.includes(name)) {
memo[name] = null
return memo
}
memo[name] = obj.headers[name]
return memo
}, {})
}
return obj
}
logger = logger.child(
{ serializers:
{ req: requestSerializer
, res: logger.serializers && logger.serializers.res || logger.constructor.stdSerializers.res
}
}
)
return function (req, res, next) {
if (filter && filter(req, res))
return next()
var id = req[propertyName]
|| req.headers[headerNameLower]
|| uuid()
var start = process.hrtime()
var prefs = {}
prefs[logName] = id
req.log = res.log = logger.child(prefs, true)
req[propertyName] = res[propertyName] = id
res.setHeader(headerName, id)
if (requestStart || verbose) {
var reqStartData = { req: req }
if (verbose) reqStartData.res = res
req.log[level](reqStartData, 'request start')
}
res.on('finish', function() {
var reqFinishData =
{ res: res
, duration: getDuration(start)
}
if (!requestStart || verbose) reqFinishData.req = req
if (additionalRequestFinishData) {
var additionReqFinishData = additionalRequestFinishData(req, res)
if (additionReqFinishData) {
Object.keys(additionReqFinishData).forEach(function(name) {
reqFinishData[name] = additionReqFinishData[name]
})
}
}
res.log[level](reqFinishData, 'request finish')
})
next()
}
}
function getDuration(start) {
var diff = process.hrtime(start)
return diff[0] * 1e3 + diff[1] * 1e-6
}