@contrast/route-metrics
Version:
`route-metrics` allows server performance, exclusive of network time, to be compared on a route-by-route basis. It was created to compare server performance with and without `@contrast/agent` being loaded and active.
98 lines (83 loc) • 2.52 kB
JavaScript
;
const os = require('os');
const shimmer = require('shimmer');
const metrics = require('../metrics-symbol');
const routeEmitter = require('../route-emitter');
//
// have to intercept all require calls but do as little as possible
// if not http.
//
function patch(m, options) {
return patchServer(m, options.name);
}
//
// patch the http/https server object and return it.
//
function patchServer(m, protocol) {
const proto = m.Server && m.Server.prototype;
// if server or its prototype aren't there then there's nothing to do
if (!proto) {
throw new Error(`cannot find ${protocol}.Server.prototype`);
}
// wrap the emitter so metrics can be captured for requests
shimmer.wrap(proto, 'emit', realEmitter => function(type, req, res) {
//
// handle "upgrade" requests? they don't return an http response, it's
// basically a socket that a stream is written to. maybe wait for the
// close on those at some point. but defer now.
//
// listen for 'request' events and patch the request object.
if (type !== 'request') {
return realEmitter.apply(this, arguments);
}
res[metrics] = getMetrics(req);
// supply a default port if none was specified
if (!res[metrics].port) {
res[metrics].port = protocol === 'http' ? 80 : 443;
}
shimmer.wrap(res, 'end', fn => function() {
// run metrics exit code
if (res[metrics]) {
const m = res[metrics];
// convert elapsed time and start time to microseconds
m.et = Number((process.hrtime.bigint() + 500n) / 1000n - m.start);
m.start = Number(m.start);
m.statusCode = res.statusCode;
m.protocol = protocol;
routeEmitter.emit('route', m);
// delete the metrics after emitting; some frameworks call end()
// more than once.
delete res[metrics];
}
// Run the real end function
return fn.apply(this, arguments);
});
return realEmitter.apply(this, arguments);
});
return m;
}
function getMetrics(req) {
const [host, port] = getHostAndPort(req);
const metrics = {
method: req.method,
host,
port,
url: req.url,
start: (process.hrtime.bigint() + 500n) / 1000n,
};
return metrics;
}
function getHostAndPort(req) {
let host;
let port;
({host} = req.headers);
if (!host) {
host = os.hostname();
}
[host, port] = host.split(':');
if (port) {
port = Number(port);
}
return [host, port];
}
module.exports = patch;