// Copyright 2020 Campbell Crowley. All rights reserved. // Author: Campbell Crowley (dev@campbellcrowley.com) const SubModule = require('./subModule.js'); /** * @description Manages watching loop timings to help find causes of * inconsistent event loops. * @augments SubModule */ class CpuWatcher extends SubModule { /** * @description Manages watching loop timings to help find causes of * inconsistent event loops. */ constructor() { super(); /** @inheritdoc */ this.myName = 'CpuWatcher'; /** * @description If a single event loop takes longer than this time, log that * something went wrong. * @private * @type {number} * @default 1000000 nanoseconds * @constant */ this._loopWarnThresh = 1000000; /** * @description Flag of whether to keep monitoring the event loop timing. * @private * @type {boolean} * @default */ this._watchLoop = false; /** * @description The timestamp of the last event loop start. * @private * @type {number} * @default */ this._lastLoopTime = 0; /** * @description How often to log periodic event loop statistics. * @private * @type {number} * @default 60 seconds * @constant */ this._loopLogFrequency = 60000000; /** * @description Timestamp of the last time loop timings were logged. * @private * @type {number} * @default */ this._loopLogTime = 0; /** * @description The amount of time the longest event loop took during the * last interval. * @private * @type {number} * @default */ this._maxLoopTime = 0; /** * @description The sum of all event loop delays during the last interval * for averaging. * @private * @type {bigint} * @default */ this._totalLoopTime = BigInt(0); /** * @description The number of event loops that took place during the last * interval. * @private * @type {bigint} * @default */ this._numLoops = BigInt(0); this._queueLoopCheck = this._queueLoopCheck.bind(this); } /** @inheritdoc */ initialize() { this._watchLoop = true; setImmediate(this._queueLoopCheck); } /** @inheritdoc */ shutdown() { this._watchLoop = false; } /** * @description Time how long the next event loop will take. Calls itself * every event loop until {@see _watchLoop} is set to false. * @private */ _queueLoopCheck() { const now = process.hrtime.bigint(); if (this._lastLoopTime != 0) { const delta = now - this._lastLoopTime; if (delta > this._loopWarnThresh) { this.warn(`Previous event loop took: ${delta} nano seconds!`); } this._maxLoopTime = (delta > this._maxLoopTime && this._maxLoopTime) || delta; this._totalLoopTime += delta; this._numLoops++; } else { this._loopLogTime = now; } if (now - this._loopLogTime > this._loopLogFrequency) { this.debug( 'Loop Timings: Max: ' + this._maxLoopTime + ' Avg: ' + (this._totalLoopTime / this._numLoops) + ' Sum: ' + this._totalLoopTime + ' Duration: ' + this._loopLogFrequency); this._maxLoopTime = 0; this._totalLoopTime = BigInt(0); this._numLoops = BigInt(0); this._loopLogTime = now; } if (!this._watchLoop) return; this._lastLoopTime = now; setImmediate(this._queueLoopCheck); } } module.exports = new CpuWatcher();