UNPKG

nestjs-otel

Version:
192 lines (191 loc) 8.31 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); 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 __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApiMetricsMiddleware = void 0; const common_1 = require("@nestjs/common"); const response_time_1 = __importDefault(require("response-time")); const urlParser = __importStar(require("url")); const metric_service_1 = require("../metrics/metric.service"); const opentelemetry_constants_1 = require("../opentelemetry.constants"); let ApiMetricsMiddleware = class ApiMetricsMiddleware { metricService; options; defaultAttributes; httpServerRequestCount; httpServerResponseCount; httpServerDuration; httpServerRequestSize; httpServerResponseSize; httpServerResponseSuccessCount; httpServerResponseErrorCount; httpClientRequestErrorCount; httpServerAbortCount; ignoreUndefinedRoutes; constructor(metricService, options = {}) { this.metricService = metricService; this.options = options; const { defaultAttributes = {}, ignoreUndefinedRoutes = false, prefix, } = options?.metrics?.apiMetrics ?? {}; this.defaultAttributes = defaultAttributes; this.ignoreUndefinedRoutes = ignoreUndefinedRoutes; // Semantic Convention this.httpServerRequestCount = this.metricService.getCounter('http.server.request.count', { description: 'Total number of HTTP requests', unit: 'requests', prefix, }); this.httpServerResponseCount = this.metricService.getCounter('http.server.response.count', { description: 'Total number of HTTP responses', unit: 'responses', prefix, }); this.httpServerAbortCount = this.metricService.getCounter('http.server.abort.count', { description: 'Total number of data transfers aborted', unit: 'requests', prefix, }); this.httpServerDuration = this.metricService.getHistogram('http.server.duration', { description: 'The duration of the inbound HTTP request', unit: 'ms', prefix, }); this.httpServerRequestSize = this.metricService.getHistogram('http.server.request.size', { description: 'Size of incoming bytes', unit: 'By', prefix, }); this.httpServerResponseSize = this.metricService.getHistogram('http.server.response.size', { description: 'Size of outgoing bytes', unit: 'By', prefix, }); // Helpers this.httpServerResponseSuccessCount = this.metricService.getCounter('http.server.response.success.count', { description: 'Total number of all successful responses', unit: 'responses', prefix, }); this.httpServerResponseErrorCount = this.metricService.getCounter('http.server.response.error.count', { description: 'Total number of all response errors', prefix, }); this.httpClientRequestErrorCount = this.metricService.getCounter('http.client.request.error.count', { description: 'Total number of client error requests', prefix, }); } use(req, res, next) { (0, response_time_1.default)((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.httpServerRequestCount.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.httpServerRequestSize.record(requestLength, attributes); this.httpServerResponseSize.record(responseLength, attributes); this.httpServerResponseCount.add(1, attributes); this.httpServerDuration.record(time, attributes); const codeClass = this.getStatusCodeClass(status); switch (codeClass) { case 'success': this.httpServerResponseSuccessCount.add(1); break; case 'redirect': // TODO: Review what should be appropriate for redirects. this.httpServerResponseSuccessCount.add(1); break; case 'client_error': this.httpClientRequestErrorCount.add(1); break; case 'server_error': this.httpServerResponseErrorCount.add(1); break; } req.on('end', () => { if (req.aborted === true) { this.httpServerAbortCount.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'; } }; exports.ApiMetricsMiddleware = ApiMetricsMiddleware; exports.ApiMetricsMiddleware = 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);