UNPKG

pm2

Version:

Production process manager for Node.JS applications with a built-in load balancer.

137 lines (122 loc) 4.26 kB
'use strict' const { MetricType } = require('../services/metrics') const { ServiceManager } = require('../serviceManager') const Debug = require('debug') const Histogram = require('../utils/metrics/histogram') const defaultOptions = { eventLoopActive: true, eventLoopDelay: true } class EventLoopHandlesRequestsMetric { constructor () { this.metricService = undefined this.logger = Debug('axm:features:metrics:eventloop') this.requestTimer = undefined this.handleTimer = undefined this.delayTimer = undefined this.delayLoopInterval = 1000 this.runtimeStatsService = undefined this.handle = undefined } init (config) { if (config === false) return if (config === undefined) { config = defaultOptions } if (config === true) { config = defaultOptions } this.metricService = ServiceManager.get('metrics') if (this.metricService === undefined) return this.logger('Failed to load metric service') this.logger('init') if (typeof process._getActiveRequests === 'function' && config.eventLoopActive === true) { const requestMetric = this.metricService.metric({ name: 'Active requests', id: 'internal/libuv/requests', historic: true }) this.requestTimer = setInterval(_ => { requestMetric.set(process._getActiveRequests().length) }, 1000) this.requestTimer.unref() } if (typeof process._getActiveHandles === 'function' && config.eventLoopActive === true) { const handleMetric = this.metricService.metric({ name: 'Active handles', id: 'internal/libuv/handles', historic: true }) this.handleTimer = setInterval(_ => { handleMetric.set(process._getActiveHandles().length) }, 1000) this.handleTimer.unref() } if (config.eventLoopDelay === false) return const histogram = new Histogram() const uvLatencyp50 = { name: 'Event Loop Latency', id: 'internal/libuv/latency/p50', type: MetricType.histogram, historic: true, implementation: histogram, handler: function () { const percentiles = this.implementation.percentiles([ 0.5 ]) if (percentiles[0.5] == null) return null return percentiles[0.5].toFixed(2) }, unit: 'ms' } const uvLatencyp95 = { name: 'Event Loop Latency p95', id: 'internal/libuv/latency/p95', type: MetricType.histogram, historic: true, implementation: histogram, handler: function () { const percentiles = this.implementation.percentiles([ 0.95 ]) if (percentiles[0.95] == null) return null return percentiles[0.95].toFixed(2) }, unit: 'ms' } this.metricService.registerMetric(uvLatencyp50) this.metricService.registerMetric(uvLatencyp95) this.runtimeStatsService = ServiceManager.get('runtimeStats') if (this.runtimeStatsService === undefined) { this.logger('runtimeStats module not found, fallbacking into pure js method') let oldTime = process.hrtime() this.delayTimer = setInterval(() => { const newTime = process.hrtime() const delay = (newTime[0] - oldTime[0]) * 1e3 + (newTime[1] - oldTime[1]) / 1e6 - this.delayLoopInterval oldTime = newTime histogram.update(delay) }, this.delayLoopInterval) this.delayTimer.unref() } else { this.logger('using runtimeStats module as data source for event loop latency') this.handle = (stats) => { if (typeof stats !== 'object' || !Array.isArray(stats.ticks)) return stats.ticks.forEach((tick) => { histogram.update(tick) }) } this.runtimeStatsService.on('data', this.handle) } } destroy () { if (this.requestTimer !== undefined) { clearInterval(this.requestTimer) } if (this.handleTimer !== undefined) { clearInterval(this.handleTimer) } if (this.delayTimer !== undefined) { clearInterval(this.delayTimer) } if (this.runtimeStatsService !== undefined) { this.runtimeStatsService.removeListener('data', this.handle) } this.logger('destroy') } } module.exports = EventLoopHandlesRequestsMetric