nestjs-otel
Version:
NestJS OpenTelemetry Library
140 lines • 7.15 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApiMetricsMiddleware = exports.DEFAULT_RESPONSE_SIZE_BUCKETS = exports.DEFAULT_REQUEST_SIZE_BUCKETS = exports.DEFAULT_LONG_RUNNING_REQUEST_BUCKETS = void 0;
const common_1 = require("@nestjs/common");
const responseTime = require("response-time");
const urlParser = require("url");
const metric_service_1 = require("../metrics/metric.service");
const opentelemetry_constants_1 = require("../opentelemetry.constants");
exports.DEFAULT_LONG_RUNNING_REQUEST_BUCKETS = [
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10,
30, 60, 120, 300, 600,
];
exports.DEFAULT_REQUEST_SIZE_BUCKETS = [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000];
exports.DEFAULT_RESPONSE_SIZE_BUCKETS = [
5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000,
];
let ApiMetricsMiddleware = class ApiMetricsMiddleware {
constructor(metricService, options = {}) {
this.metricService = metricService;
this.options = options;
this.defaultLongRunningRequestBuckets = exports.DEFAULT_LONG_RUNNING_REQUEST_BUCKETS;
this.defaultRequestSizeBuckets = exports.DEFAULT_REQUEST_SIZE_BUCKETS;
this.defaultResponseSizeBuckets = exports.DEFAULT_RESPONSE_SIZE_BUCKETS;
this.requestTotal = this.metricService.getCounter('http_request_total', {
description: 'Total number of HTTP requests',
});
this.responseTotal = this.metricService.getCounter('http_response_total', {
description: 'Total number of HTTP responses',
});
this.responseSuccessTotal = this.metricService.getCounter('http_response_success_total', {
description: 'Total number of all successful responses',
});
this.responseErrorTotal = this.metricService.getCounter('http_response_error_total', {
description: 'Total number of all response errors',
});
this.responseClientErrorTotal = this.metricService.getCounter('http_client_error_total', {
description: 'Total number of client error requests',
});
this.responseServerErrorTotal = this.metricService.getCounter('http_server_error_total', {
description: 'Total number of server error requests',
});
this.serverAbortsTotal = this.metricService.getCounter('http_server_aborts_total', {
description: 'Total number of data transfers aborted',
});
const { timeBuckets = [], requestSizeBuckets = [], responseSizeBuckets = [], defaultAttributes = {}, ignoreUndefinedRoutes = false, } = options?.metrics?.apiMetrics;
this.defaultAttributes = defaultAttributes;
this.ignoreUndefinedRoutes = ignoreUndefinedRoutes;
this.requestDuration = this.metricService.getHistogram('http_request_duration_seconds', {
boundaries: timeBuckets.length > 0 ? timeBuckets : this.defaultLongRunningRequestBuckets,
description: 'HTTP latency value recorder in seconds',
});
this.requestSizeHistogram = this.metricService.getHistogram('http_request_size_bytes', {
boundaries: requestSizeBuckets.length > 0 ? requestSizeBuckets : this.defaultRequestSizeBuckets,
description: 'Current total of incoming bytes',
});
this.responseSizeHistogram = this.metricService.getHistogram('http_response_size_bytes', {
boundaries: responseSizeBuckets.length > 0 ? responseSizeBuckets : this.defaultResponseSizeBuckets,
description: 'Current total of outgoing bytes',
});
}
use(req, res, next) {
responseTime((req, res, time) => {
const { route, url, method } = req;
let path;
if (route) {
path = route.path;
}
else if (this.ignoreUndefinedRoutes) {
return;
}
else {
path = urlParser.parse(url).pathname;
}
this.requestTotal.add(1, { method, path });
const requestLength = parseInt(req.headers['content-length'], 10) || 0;
const responseLength = parseInt(res.getHeader('Content-Length'), 10) || 0;
const status = res.statusCode || 500;
const attributes = {
method, status, path, ...this.defaultAttributes,
};
this.requestSizeHistogram.record(requestLength, attributes);
this.responseSizeHistogram.record(responseLength, attributes);
this.responseTotal.add(1, attributes);
this.requestDuration.record(time / 1000, attributes);
const codeClass = this.getStatusCodeClass(status);
switch (codeClass) {
case 'success':
this.responseSuccessTotal.add(1);
break;
case 'redirect':
this.responseSuccessTotal.add(1);
break;
case 'client_error':
this.responseErrorTotal.add(1);
this.responseClientErrorTotal.add(1);
break;
case 'server_error':
this.responseErrorTotal.add(1);
this.responseServerErrorTotal.add(1);
break;
}
req.on('end', () => {
if (req.aborted === true) {
this.serverAbortsTotal.add(1);
}
});
})(req, res, next);
}
getStatusCodeClass(code) {
if (code < 200)
return 'info';
if (code < 300)
return 'success';
if (code < 400)
return 'redirect';
if (code < 500)
return 'client_error';
return 'server_error';
}
};
ApiMetricsMiddleware = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)(metric_service_1.MetricService)),
__param(1, (0, common_1.Inject)(opentelemetry_constants_1.OPENTELEMETRY_MODULE_OPTIONS)),
__metadata("design:paramtypes", [metric_service_1.MetricService, Object])
], ApiMetricsMiddleware);
exports.ApiMetricsMiddleware = ApiMetricsMiddleware;
//# sourceMappingURL=api-metrics.middleware.js.map