You can do this with response-time plugin but I wanted something a bit more flexible and can easily be used on select routes as middleware, so I made:
import onHeaders from 'on-headers';
import net from "net";
import moment from "moment/moment";
import ApiLog from "../models/RequestLog";
// Slow is 10 seconds
const defaultSlowTime = 10000;
function _getTimeDiff(hrtime) {
var diff = process.hrtime(hrtime);
return diff[0] * 1e3 + diff[1] * 1e-6
}
function _ipIsPrivate(ip) {
if (ip.substring(0,7) === "::ffff:")
ip = ip.substring(7);
if (net.isIPv4(ip)) {
// 10.0.0.0 - 10.255.255.255 || 172.16.0.0 - 172.31.255.255 || 192.168.0.0 - 192.168.255.255
return /^(10)\.(.*)\.(.*)\.(.*)$/.test(ip) || /^(172)\.(1[6-9]|2[0-9]|3[0-1])\.(.*)\.(.*)$/.test(ip) || /^(192)\.(168)\.(.*)\.(.*)$/.test(ip)
}
// else: ip is IPv6
const firstWord = ip.split(":").find(el => !!el); //get first not empty word
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Range: fec0 - feff
if (/^fe[c-f][0-f]$/.test(firstWord))
return true;
// These days Unique Local Addresses (ULA) are used in place of Site Local.
// Range: fc00 - fcff
else if (/^fc[0-f]{2}$/.test(firstWord))
return true;
// Range: fd00 - fcff
else if (/^fd[0-f]{2}$/.test(firstWord))
return true;
// Link local addresses (prefixed with fe80) are not routable
else if (firstWord === "fe80")
return true;
// Discard Prefix
else if (firstWord === "100")
return true;
// Any other IP address is not Unique Local Address (ULA)
return false;
}
export default class {
static log(onlySlow ) {
const slowTime = onlySlow === true ? defaultSlowTime : (onlySlow === false ? 0 : onlySlow);
return (req, res, next) => {
onHeaders(res, function onHeaders() {
const {
log: {
userId = null,
} = {},
ip,
originalUrl,
startedAt,
hrtime,
} = req;
const time = _getTimeDiff(hrtime);
// TODO do we want to add configuration to omit private IPs such as dev computers?
const isPrivateIp = _ipIsPrivate(ip);
if (slowTime < time) {
// async so we don't stop request
ApiLog.createLog({
userId,
startedAt: startedAt.toISOString(),
endedAt: moment().toISOString(),
ip: ip,
url: originalUrl,
duration: time,
});
}
});
next();
}
}
}
And added
app.use((req, res, next) => {
// This is our own personal version of response-time that we can attach to specific routes
req.hrtime = process.hrtime();
req.startedAt = moment();
});
in my base expressjs route file, now I can easily log certain routes by doing:
router.get('/v2/events/all/:year/:month',
RequestLogMiddleware.log(true),
celebrate(calendarEventSchema.retrieveAllEventsByMonth),
CalendarEventController.retrieveAllEventsByMonth
);