179 lines
6.3 KiB
JavaScript
179 lines
6.3 KiB
JavaScript
'use strict';
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.HttpMetricsConfig = void 0;
|
|
const shimmer = require("shimmer");
|
|
const debug_1 = require("debug");
|
|
const configuration_1 = require("../configuration");
|
|
const serviceManager_1 = require("../serviceManager");
|
|
const histogram_1 = require("../utils/metrics/histogram");
|
|
const requireMiddle = require("require-in-the-middle");
|
|
const metrics_1 = require("../services/metrics");
|
|
class HttpMetricsConfig {
|
|
}
|
|
exports.HttpMetricsConfig = HttpMetricsConfig;
|
|
class HttpMetrics {
|
|
constructor() {
|
|
this.defaultConf = {
|
|
http: true
|
|
};
|
|
this.metrics = new Map();
|
|
this.logger = (0, debug_1.default)('axm:features:metrics:http');
|
|
this.modules = {};
|
|
}
|
|
init(config) {
|
|
if (config === false)
|
|
return;
|
|
if (config === undefined) {
|
|
config = this.defaultConf;
|
|
}
|
|
if (typeof config !== 'object') {
|
|
config = this.defaultConf;
|
|
}
|
|
this.logger('init');
|
|
configuration_1.default.configureModule({
|
|
latency: true
|
|
});
|
|
this.metricService = serviceManager_1.ServiceManager.get('metrics');
|
|
if (this.metricService === undefined)
|
|
return this.logger(`Failed to load metric service`);
|
|
this.logger('hooking to require');
|
|
this.hookRequire();
|
|
}
|
|
registerHttpMetric() {
|
|
if (this.metricService === undefined)
|
|
return this.logger(`Failed to load metric service`);
|
|
const histogram = new histogram_1.default();
|
|
const p50 = {
|
|
name: `HTTP Mean Latency`,
|
|
id: 'internal/http/builtin/latency/p50',
|
|
type: metrics_1.MetricType.histogram,
|
|
historic: true,
|
|
implementation: histogram,
|
|
unit: 'ms',
|
|
handler: () => {
|
|
const percentiles = histogram.percentiles([0.5]);
|
|
return percentiles[0.5];
|
|
}
|
|
};
|
|
const p95 = {
|
|
name: `HTTP P95 Latency`,
|
|
id: 'internal/http/builtin/latency/p95',
|
|
type: metrics_1.MetricType.histogram,
|
|
historic: true,
|
|
implementation: histogram,
|
|
handler: () => {
|
|
const percentiles = histogram.percentiles([0.95]);
|
|
return percentiles[0.95];
|
|
},
|
|
unit: 'ms'
|
|
};
|
|
const meter = {
|
|
name: 'HTTP',
|
|
historic: true,
|
|
id: 'internal/http/builtin/reqs',
|
|
unit: 'req/min'
|
|
};
|
|
this.metricService.registerMetric(p50);
|
|
this.metricService.registerMetric(p95);
|
|
this.metrics.set('http.latency', histogram);
|
|
this.metrics.set('http.meter', this.metricService.meter(meter));
|
|
}
|
|
registerHttpsMetric() {
|
|
if (this.metricService === undefined)
|
|
return this.logger(`Failed to load metric service`);
|
|
const histogram = new histogram_1.default();
|
|
const p50 = {
|
|
name: `HTTPS Mean Latency`,
|
|
id: 'internal/https/builtin/latency/p50',
|
|
type: metrics_1.MetricType.histogram,
|
|
historic: true,
|
|
implementation: histogram,
|
|
unit: 'ms',
|
|
handler: () => {
|
|
const percentiles = histogram.percentiles([0.5]);
|
|
return percentiles[0.5];
|
|
}
|
|
};
|
|
const p95 = {
|
|
name: `HTTPS P95 Latency`,
|
|
id: 'internal/https/builtin/latency/p95',
|
|
type: metrics_1.MetricType.histogram,
|
|
historic: true,
|
|
implementation: histogram,
|
|
handler: () => {
|
|
const percentiles = histogram.percentiles([0.95]);
|
|
return percentiles[0.95];
|
|
},
|
|
unit: 'ms'
|
|
};
|
|
const meter = {
|
|
name: 'HTTPS',
|
|
historic: true,
|
|
id: 'internal/https/builtin/reqs',
|
|
unit: 'req/min'
|
|
};
|
|
this.metricService.registerMetric(p50);
|
|
this.metricService.registerMetric(p95);
|
|
this.metrics.set('https.latency', histogram);
|
|
this.metrics.set('https.meter', this.metricService.meter(meter));
|
|
}
|
|
destroy() {
|
|
if (this.modules.http !== undefined) {
|
|
this.logger('unwraping http module');
|
|
shimmer.unwrap(this.modules.http, 'emit');
|
|
this.modules.http = undefined;
|
|
}
|
|
if (this.modules.https !== undefined) {
|
|
this.logger('unwraping https module');
|
|
shimmer.unwrap(this.modules.https, 'emit');
|
|
this.modules.https = undefined;
|
|
}
|
|
if (this.hooks) {
|
|
this.hooks.unhook();
|
|
}
|
|
this.logger('destroy');
|
|
}
|
|
hookHttp(nodule, name) {
|
|
if (nodule.Server === undefined || nodule.Server.prototype === undefined)
|
|
return;
|
|
if (this.modules[name] !== undefined)
|
|
return this.logger(`Module ${name} already hooked`);
|
|
this.logger(`Hooking to ${name} module`);
|
|
this.modules[name] = nodule.Server.prototype;
|
|
if (name === 'http') {
|
|
this.registerHttpMetric();
|
|
}
|
|
else if (name === 'https') {
|
|
this.registerHttpsMetric();
|
|
}
|
|
const self = this;
|
|
shimmer.wrap(nodule.Server.prototype, 'emit', (original) => {
|
|
return function (event, req, res) {
|
|
if (event !== 'request')
|
|
return original.apply(this, arguments);
|
|
const meter = self.metrics.get(`${name}.meter`);
|
|
if (meter !== undefined) {
|
|
meter.mark();
|
|
}
|
|
const latency = self.metrics.get(`${name}.latency`);
|
|
if (latency === undefined)
|
|
return original.apply(this, arguments);
|
|
if (res === undefined || res === null)
|
|
return original.apply(this, arguments);
|
|
const startTime = Date.now();
|
|
res.once('finish', _ => {
|
|
latency.update(Date.now() - startTime);
|
|
});
|
|
return original.apply(this, arguments);
|
|
};
|
|
});
|
|
}
|
|
hookRequire() {
|
|
this.hooks = requireMiddle(['http', 'https'], (exports, name) => {
|
|
this.hookHttp(exports, name);
|
|
return exports;
|
|
});
|
|
}
|
|
}
|
|
exports.default = HttpMetrics;
|