import * as express from 'express' export interface Options { ignore: string[] disableRouteCounter: boolean disableErrorCounter: boolean disableDurationCounter: boolean disableDefaultMetrics: boolean } export class Metrics { public readonly _ignore: string[] public readonly _disableRouteCounter: boolean public readonly _disableErrorCounter: boolean public readonly _disableDurationCounter: boolean public readonly _disableDefaultMetrics: boolean public readonly _client: any public readonly _httpRequestDurationMicroseconds: any public readonly _numOfRequests: any public readonly _numOfErrors: any constructor (options: Partial<Options> = {}) { this._client = require('prom-client') if (typeof options.ignore !== 'undefined') { this._ignore = options.ignore this._ignore.push('/_metrics') this._ignore.push('/favicon.ico') } else { this._ignore = ['/_metrics', '/favicon.ico'] } if (typeof options.disableRouteCounter !== 'undefined') { this._disableRouteCounter = options.disableRouteCounter } else { this._disableRouteCounter = false } if (typeof options.disableErrorCounter !== 'undefined') { this._disableErrorCounter = options.disableErrorCounter } else { this._disableErrorCounter = false } if (typeof options.disableDurationCounter !== 'undefined') { this._disableDurationCounter = options.disableDurationCounter } else { this._disableDurationCounter = false } if (typeof options.disableDefaultMetrics !== 'undefined') { this._disableDefaultMetrics = options.disableDefaultMetrics } else { this._disableDefaultMetrics = false } if (!this._disableDefaultMetrics) { this._client._collectDefaultMetrics = this._client.collectDefaultMetrics } if (!this._disableErrorCounter) { this._numOfErrors = new this._client.Counter({ name: 'numOfErrors', help: 'Number of errors', labelNames: ['error'] }) } if (!this._disableRouteCounter) { this._numOfRequests = new this._client.Counter({ name: 'numOfRequests', help: 'Number of requests made to a route', labelNames: ['route'] }) } if (!this._disableDurationCounter) { this._httpRequestDurationMicroseconds = new this._client.Histogram({ name: 'http_request_duration_ms', help: 'Duration of HTTP requests in ms', labelNames: ['method', 'route', 'code'], buckets: [0.10, 5, 15, 50, 100, 200, 300, 400, 500] }) } } public collect = (req: express.Request, res: express.Response, next: express.NextFunction): void => { res.locals.startEpoch = Date.now() res.on('finish', () => { if (!this._ignore.includes(req.originalUrl)) { const responseTimeInMs = Date.now() - res.locals.startEpoch if (!this._disableDurationCounter) { this._httpRequestDurationMicroseconds .labels(req.method, req.originalUrl, res.statusCode.toString()) .observe(responseTimeInMs) } if (!this._disableRouteCounter) { this._numOfRequests.inc({ route: req.originalUrl }) } if (res.statusCode >= 400) { if (!this._disableErrorCounter) { this._numOfErrors.inc({ error: res.statusCode }) } } } }) next() } public endpoint = (req: express.Request, res: express.Response): void => { if (!this._disableDefaultMetrics) { this._client._collectDefaultMetrics() } res.set('Content-Type', this._client.register.contentType) res.status(200) res.end(this._client.register.metrics()) } } export default Metrics