"use strict"; var os = require("os"); var Constants = require("../Declarations/Constants"); var AutoCollectPerformance = (function () { /** * @param enableLiveMetricsCounters - enable sending additional live metrics information (dependency metrics, exception metrics, committed memory) */ function AutoCollectPerformance(client, collectionInterval, enableLiveMetricsCounters) { if (collectionInterval === void 0) { collectionInterval = 60000; } if (enableLiveMetricsCounters === void 0) { enableLiveMetricsCounters = false; } this._lastIntervalRequestExecutionTime = 0; // the sum of durations which took place during from app start until last interval this._lastIntervalDependencyExecutionTime = 0; if (!AutoCollectPerformance.INSTANCE) { AutoCollectPerformance.INSTANCE = this; } this._isInitialized = false; this._client = client; this._collectionInterval = collectionInterval; this._enableLiveMetricsCounters = enableLiveMetricsCounters; } AutoCollectPerformance.prototype.enable = function (isEnabled, collectionInterval) { var _this = this; this._isEnabled = isEnabled; if (this._isEnabled && !this._isInitialized) { this._isInitialized = true; } if (isEnabled) { if (!this._handle) { this._lastCpus = os.cpus(); this._lastRequests = { totalRequestCount: AutoCollectPerformance._totalRequestCount, totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount, time: +new Date }; this._lastDependencies = { totalDependencyCount: AutoCollectPerformance._totalDependencyCount, totalFailedDependencyCount: AutoCollectPerformance._totalFailedDependencyCount, time: +new Date }; this._lastExceptions = { totalExceptionCount: AutoCollectPerformance._totalExceptionCount, time: +new Date }; if (typeof process.cpuUsage === 'function') { this._lastAppCpuUsage = process.cpuUsage(); } this._lastHrtime = process.hrtime(); this._collectionInterval = collectionInterval || this._collectionInterval; this._handle = setInterval(function () { return _this.trackPerformance(); }, this._collectionInterval); this._handle.unref(); // Allow the app to terminate even while this loop is going on } } else { if (this._handle) { clearInterval(this._handle); this._handle = undefined; } } }; AutoCollectPerformance.countRequest = function (duration, success) { var durationMs; if (!AutoCollectPerformance.isEnabled()) { return; } if (typeof duration === 'string') { // dependency duration is passed in as "00:00:00.123" by autocollectors durationMs = +new Date('1970-01-01T' + duration + 'Z'); // convert to num ms, returns NaN if wrong } else if (typeof duration === 'number') { durationMs = duration; } else { return; } AutoCollectPerformance._intervalRequestExecutionTime += durationMs; if (success === false) { AutoCollectPerformance._totalFailedRequestCount++; } AutoCollectPerformance._totalRequestCount++; }; AutoCollectPerformance.countException = function () { AutoCollectPerformance._totalExceptionCount++; }; AutoCollectPerformance.countDependency = function (duration, success) { var durationMs; if (!AutoCollectPerformance.isEnabled()) { return; } if (typeof duration === 'string') { // dependency duration is passed in as "00:00:00.123" by autocollectors durationMs = +new Date('1970-01-01T' + duration + 'Z'); // convert to num ms, returns NaN if wrong } else if (typeof duration === 'number') { durationMs = duration; } else { return; } AutoCollectPerformance._intervalDependencyExecutionTime += durationMs; if (success === false) { AutoCollectPerformance._totalFailedDependencyCount++; } AutoCollectPerformance._totalDependencyCount++; }; AutoCollectPerformance.prototype.isInitialized = function () { return this._isInitialized; }; AutoCollectPerformance.isEnabled = function () { return AutoCollectPerformance.INSTANCE && AutoCollectPerformance.INSTANCE._isEnabled; }; AutoCollectPerformance.prototype.trackPerformance = function () { this._trackCpu(); this._trackMemory(); this._trackNetwork(); this._trackDependencyRate(); this._trackExceptionRate(); }; AutoCollectPerformance.prototype._trackCpu = function () { // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary // to find the delta since the last measurement var cpus = os.cpus(); if (cpus && cpus.length && this._lastCpus && cpus.length === this._lastCpus.length) { var totalUser = 0; var totalSys = 0; var totalNice = 0; var totalIdle = 0; var totalIrq = 0; for (var i = 0; !!cpus && i < cpus.length; i++) { var cpu = cpus[i]; var lastCpu = this._lastCpus[i]; var name = "% cpu(" + i + ") "; var model = cpu.model; var speed = cpu.speed; var times = cpu.times; var lastTimes = lastCpu.times; // user cpu time (or) % CPU time spent in user space var user = (times.user - lastTimes.user) || 0; totalUser += user; // system cpu time (or) % CPU time spent in kernel space var sys = (times.sys - lastTimes.sys) || 0; totalSys += sys; // user nice cpu time (or) % CPU time spent on low priority processes var nice = (times.nice - lastTimes.nice) || 0; totalNice += nice; // idle cpu time (or) % CPU time spent idle var idle = (times.idle - lastTimes.idle) || 0; totalIdle += idle; // irq (or) % CPU time spent servicing/handling hardware interrupts var irq = (times.irq - lastTimes.irq) || 0; totalIrq += irq; } // Calculate % of total cpu time (user + system) this App Process used (Only supported by node v6.1.0+) var appCpuPercent = undefined; if (typeof process.cpuUsage === 'function') { var appCpuUsage = process.cpuUsage(); var hrtime = process.hrtime(); var totalApp = ((appCpuUsage.user - this._lastAppCpuUsage.user) + (appCpuUsage.system - this._lastAppCpuUsage.system)) || 0; if (typeof this._lastHrtime !== 'undefined' && this._lastHrtime.length === 2) { var elapsedTime = ((hrtime[0] - this._lastHrtime[0]) * 1e6 + (hrtime[1] - this._lastHrtime[1]) / 1e3) || 0; // convert to microseconds appCpuPercent = 100 * totalApp / (elapsedTime * cpus.length); } // Set previous this._lastAppCpuUsage = appCpuUsage; this._lastHrtime = hrtime; } var combinedTotal = (totalUser + totalSys + totalNice + totalIdle + totalIrq) || 1; this._client.trackMetric({ name: Constants.PerformanceCounter.PROCESSOR_TIME, value: ((combinedTotal - totalIdle) / combinedTotal) * 100 }); this._client.trackMetric({ name: Constants.PerformanceCounter.PROCESS_TIME, value: appCpuPercent || ((totalUser / combinedTotal) * 100) }); } this._lastCpus = cpus; }; AutoCollectPerformance.prototype._trackMemory = function () { var freeMem = os.freemem(); var usedMem = process.memoryUsage().rss; var committedMemory = os.totalmem() - freeMem; this._client.trackMetric({ name: Constants.PerformanceCounter.PRIVATE_BYTES, value: usedMem }); this._client.trackMetric({ name: Constants.PerformanceCounter.AVAILABLE_BYTES, value: freeMem }); // Only supported by quickpulse service if (this._enableLiveMetricsCounters) { this._client.trackMetric({ name: Constants.QuickPulseCounter.COMMITTED_BYTES, value: committedMemory }); } }; AutoCollectPerformance.prototype._trackNetwork = function () { // track total request counters var lastRequests = this._lastRequests; var requests = { totalRequestCount: AutoCollectPerformance._totalRequestCount, totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount, time: +new Date }; var intervalRequests = (requests.totalRequestCount - lastRequests.totalRequestCount) || 0; var intervalFailedRequests = (requests.totalFailedRequestCount - lastRequests.totalFailedRequestCount) || 0; var elapsedMs = requests.time - lastRequests.time; var elapsedSeconds = elapsedMs / 1000; var averageRequestExecutionTime = ((AutoCollectPerformance._intervalRequestExecutionTime - this._lastIntervalRequestExecutionTime) / intervalRequests) || 0; // default to 0 in case no requests in this interval this._lastIntervalRequestExecutionTime = AutoCollectPerformance._intervalRequestExecutionTime; // reset if (elapsedMs > 0) { var requestsPerSec = intervalRequests / elapsedSeconds; var failedRequestsPerSec = intervalFailedRequests / elapsedSeconds; this._client.trackMetric({ name: Constants.PerformanceCounter.REQUEST_RATE, value: requestsPerSec }); // Only send duration to live metrics if it has been updated! if (!this._enableLiveMetricsCounters || intervalRequests > 0) { this._client.trackMetric({ name: Constants.PerformanceCounter.REQUEST_DURATION, value: averageRequestExecutionTime }); } // Only supported by quickpulse service if (this._enableLiveMetricsCounters) { this._client.trackMetric({ name: Constants.QuickPulseCounter.REQUEST_FAILURE_RATE, value: failedRequestsPerSec }); } } this._lastRequests = requests; }; // Static counter is accumulated externally. Report the rate to client here // Note: This is currently only used with QuickPulse client AutoCollectPerformance.prototype._trackDependencyRate = function () { if (this._enableLiveMetricsCounters) { var lastDependencies = this._lastDependencies; var dependencies = { totalDependencyCount: AutoCollectPerformance._totalDependencyCount, totalFailedDependencyCount: AutoCollectPerformance._totalFailedDependencyCount, time: +new Date }; var intervalDependencies = (dependencies.totalDependencyCount - lastDependencies.totalDependencyCount) || 0; var intervalFailedDependencies = (dependencies.totalFailedDependencyCount - lastDependencies.totalFailedDependencyCount) || 0; var elapsedMs = dependencies.time - lastDependencies.time; var elapsedSeconds = elapsedMs / 1000; var averageDependencyExecutionTime = ((AutoCollectPerformance._intervalDependencyExecutionTime - this._lastIntervalDependencyExecutionTime) / intervalDependencies) || 0; this._lastIntervalDependencyExecutionTime = AutoCollectPerformance._intervalDependencyExecutionTime; // reset if (elapsedMs > 0) { var dependenciesPerSec = intervalDependencies / elapsedSeconds; var failedDependenciesPerSec = intervalFailedDependencies / elapsedSeconds; this._client.trackMetric({ name: Constants.QuickPulseCounter.DEPENDENCY_RATE, value: dependenciesPerSec }); this._client.trackMetric({ name: Constants.QuickPulseCounter.DEPENDENCY_FAILURE_RATE, value: failedDependenciesPerSec }); // redundant check for livemetrics, but kept for consistency w/ requests // Only send duration to live metrics if it has been updated! if (!this._enableLiveMetricsCounters || intervalDependencies > 0) { this._client.trackMetric({ name: Constants.QuickPulseCounter.DEPENDENCY_DURATION, value: averageDependencyExecutionTime }); } } this._lastDependencies = dependencies; } }; // Static counter is accumulated externally. Report the rate to client here // Note: This is currently only used with QuickPulse client AutoCollectPerformance.prototype._trackExceptionRate = function () { if (this._enableLiveMetricsCounters) { var lastExceptions = this._lastExceptions; var exceptions = { totalExceptionCount: AutoCollectPerformance._totalExceptionCount, time: +new Date }; var intervalExceptions = (exceptions.totalExceptionCount - lastExceptions.totalExceptionCount) || 0; var elapsedMs = exceptions.time - lastExceptions.time; var elapsedSeconds = elapsedMs / 1000; if (elapsedMs > 0) { var exceptionsPerSec = intervalExceptions / elapsedSeconds; this._client.trackMetric({ name: Constants.QuickPulseCounter.EXCEPTION_RATE, value: exceptionsPerSec }); } this._lastExceptions = exceptions; } }; AutoCollectPerformance.prototype.dispose = function () { AutoCollectPerformance.INSTANCE = null; this.enable(false); this._isInitialized = false; }; AutoCollectPerformance._totalRequestCount = 0; AutoCollectPerformance._totalFailedRequestCount = 0; AutoCollectPerformance._lastRequestExecutionTime = 0; AutoCollectPerformance._totalDependencyCount = 0; AutoCollectPerformance._totalFailedDependencyCount = 0; AutoCollectPerformance._lastDependencyExecutionTime = 0; AutoCollectPerformance._totalExceptionCount = 0; AutoCollectPerformance._intervalDependencyExecutionTime = 0; AutoCollectPerformance._intervalRequestExecutionTime = 0; return AutoCollectPerformance; }()); module.exports = AutoCollectPerformance; //# sourceMappingURL=Performance.js.map