128 lines
5.2 KiB
JavaScript
128 lines
5.2 KiB
JavaScript
'use strict';
|
|
|
|
var assert = require('assert-plus');
|
|
var hrTimeDurationInMs = require('./utils/hrTimeDurationInMs');
|
|
|
|
/**
|
|
* Timing internals
|
|
*
|
|
* Timings are also saved when there is no handler in the given category.
|
|
* Some handler categories are optional, for example there is no
|
|
* `use` and `route` for 404.
|
|
*
|
|
* @private
|
|
*
|
|
* req._timeStart - request lifecycle started in restify
|
|
* req._timePreStart - pre handlers started
|
|
* req._timePreEnd - all pre handlers finished
|
|
* req._timeUseStart - use handlers started
|
|
* req._timeUseEnd - all use handlers finished
|
|
* req._timeRouteStart - route handlers started
|
|
* req._timeRouteEnd - all route handlers finished
|
|
* req._timeFlushed - request flushed, may happens before handlers finished
|
|
* req._timeFinished - both all handlers finished and request flushed
|
|
*/
|
|
|
|
///--- API
|
|
|
|
/**
|
|
* The module includes the following plugins to be used with restify's `after`
|
|
* event, e.g., `server.on('after', restify.plugins.metrics());`:
|
|
*
|
|
* A plugin that listens to the server's after event and emits information
|
|
* about that request.
|
|
*
|
|
* @public
|
|
* @function metrics
|
|
* @param {Object} opts - an options obj
|
|
* @param {Server} opts.server - restify server
|
|
* @param {createMetrics~callback} callback - a callback fn
|
|
* @returns {Function} returns a function suitable to be used
|
|
* with restify server's `after` event
|
|
* @example
|
|
* server.on('after', restify.plugins.metrics({ server: server },
|
|
* function (err, metrics, req, res, route) {
|
|
* // metrics is an object containing information about the request
|
|
* }));
|
|
*/
|
|
function createMetrics(opts, callback) {
|
|
assert.object(opts, 'opts');
|
|
assert.object(opts.server, 'opts.server');
|
|
assert.func(callback, 'callback');
|
|
|
|
return function metrics(req, res, route, err) {
|
|
var data = {
|
|
// response status code. in most cases this should be a proper
|
|
// http status code, but in the case of an uncaughtException it can
|
|
// be undefined. otherwise, in most normal scenarios, even calling
|
|
// res.send() or res.end() should result in a 200 by default.
|
|
statusCode: res.statusCode,
|
|
// REST verb
|
|
method: req.method,
|
|
// overall request latency
|
|
totalLatency: hrTimeDurationInMs(req._timeStart, req._timeFinished),
|
|
latency: hrTimeDurationInMs(req._timeStart, req._timeFlushed),
|
|
preLatency: hrTimeDurationInMs(req._timePreStart, req._timePreEnd),
|
|
useLatency: hrTimeDurationInMs(req._timeUseStart, req._timeUseEnd),
|
|
routeLatency: hrTimeDurationInMs(
|
|
req._timeRouteStart,
|
|
req._timeRouteEnd
|
|
),
|
|
// the cleaned up url path
|
|
// e.g., /foo?a=1 => /foo
|
|
path: req.path(),
|
|
// connection state can currently only have the following values:
|
|
// 'close' | undefined.
|
|
//
|
|
// if the connection state is 'close'
|
|
// the status code will be set to 444
|
|
// it is possible to get a 200 statusCode with a connectionState
|
|
// value of 'close'. i.e., the client timed out,
|
|
// but restify thinks it "sent" a response. connectionState should
|
|
// always be the primary source of truth here, and check it first
|
|
// before consuming statusCode. otherwise, it may result in skewed
|
|
// metrics.
|
|
connectionState: req.connectionState && req.connectionState(),
|
|
unfinishedRequests:
|
|
opts.server.inflightRequests && opts.server.inflightRequests(),
|
|
inflightRequests:
|
|
opts.server.inflightRequests && opts.server.inflightRequests()
|
|
};
|
|
|
|
return callback(err, data, req, res, route);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Callback used by metrics plugin
|
|
* @callback metrics~callback
|
|
* @param {Error} err
|
|
* @param {Object} metrics - metrics about the request
|
|
* @param {Number} metrics.statusCode status code of the response. can be
|
|
* undefined in the case of an uncaughtException
|
|
* @param {String} metrics.method http request verb
|
|
* @param {Number} metrics.totalLatency latency includes both request is flushed
|
|
* and all handlers finished
|
|
* @param {Number} metrics.latency latency when request is flushed
|
|
* @param {Number|null} metrics.preLatency pre handlers latency
|
|
* @param {Number|null} metrics.useLatency use handlers latency
|
|
* @param {Number|null} metrics.routeLatency route handlers latency
|
|
* @param {String} metrics.path `req.path()` value
|
|
* @param {Number} metrics.inflightRequests Number of inflight requests pending
|
|
* in restify.
|
|
* @param {Number} metrics.unifinishedRequests Same as `inflightRequests`
|
|
* @param {String} metrics.connectionState can be either `'close'` or
|
|
* `undefined`. If this value is set, err will be a
|
|
* corresponding `RequestCloseError`.
|
|
* If connectionState is either
|
|
* `'close'`, then the `statusCode` is not applicable since the
|
|
* connection was severed before a response was written.
|
|
* @param {Request} req the request obj
|
|
* @param {Response} res the response obj
|
|
* @param {Route} route the route obj that serviced the request
|
|
*/
|
|
|
|
///-- Exports
|
|
|
|
module.exports = createMetrics;
|