const { MetricTypes } = require('./Metric'); const units = require('../util/units'); const EWMA = require('../util/ExponentiallyMovingWeightedAverage'); const RATE_UNIT = units.SECONDS; const TICK_INTERVAL = 5 * units.SECONDS; /** * Things that are measured as events / interval. * @implements {Metric} * @example * var Measured = require('measured') * var meter = new Measured.Meter(); * http.createServer(function(req, res) { * meter.mark(); * }); */ class Meter { /** * @param {MeterProperties} [properties] see {@link MeterProperties}. */ constructor(properties) { this._properties = properties || {}; this._initializeState(); if (!this._properties.keepAlive) { this.unref(); } } /** * Initializes the state of this Metric * @private */ _initializeState() { this._rateUnit = this._properties.rateUnit || RATE_UNIT; this._tickInterval = this._properties.tickInterval || TICK_INTERVAL; if (this._properties.getTime) { this._getTime = this._properties.getTime; } this._m1Rate = this._properties.m1Rate || new EWMA(units.MINUTES, this._tickInterval); this._m5Rate = this._properties.m5Rate || new EWMA(5 * units.MINUTES, this._tickInterval); this._m15Rate = this._properties.m15Rate || new EWMA(15 * units.MINUTES, this._tickInterval); this._count = 0; this._currentSum = 0; this._startTime = this._getTime(); this._lastToJSON = this._getTime(); this._interval = setInterval(this._tick.bind(this), TICK_INTERVAL); } /** * Register n events as having just occured. Defaults to 1. * @param {number} [n] */ mark(n) { if (!this._interval) { this.start(); } n = n || 1; this._count += n; this._currentSum += n; this._m1Rate.update(n); this._m5Rate.update(n); this._m15Rate.update(n); } start() {} end() { clearInterval(this._interval); this._interval = null; } /** * Refs the backing timer again. Idempotent. */ ref() { if (this._interval && this._interval.ref) { this._interval.ref(); } } /** * Unrefs the backing timer. The meter will not keep the event loop alive. Idempotent. */ unref() { if (this._interval && this._interval.unref) { this._interval.unref(); } } _tick() { this._m1Rate.tick(); this._m5Rate.tick(); this._m15Rate.tick(); } /** * Resets all values. Meters initialized with custom options will be reset to the default settings (patch welcome). */ reset() { this.end(); this._initializeState(); } meanRate() { if (this._count === 0) { return 0; } const elapsed = this._getTime() - this._startTime; return this._count / elapsed * this._rateUnit; } currentRate() { const currentSum = this._currentSum; const duration = this._getTime() - this._lastToJSON; const currentRate = currentSum / duration * this._rateUnit; this._currentSum = 0; this._lastToJSON = this._getTime(); // currentRate could be NaN if duration was 0, so fix that return currentRate || 0; } /** * @return {MeterData} */ toJSON() { return { mean: this.meanRate(), count: this._count, currentRate: this.currentRate(), '1MinuteRate': this._m1Rate.rate(this._rateUnit), '5MinuteRate': this._m5Rate.rate(this._rateUnit), '15MinuteRate': this._m15Rate.rate(this._rateUnit) }; } _getTime() { if (!process.hrtime) { return new Date().getTime(); } const hrtime = process.hrtime(); return hrtime[0] * 1000 + hrtime[1] / (1000 * 1000); } /** * The type of the Metric Impl. {@link MetricTypes}. * @return {string} The type of the Metric Impl. */ getType() { return MetricTypes.METER; } } module.exports = Meter; /** * * @interface MeterProperties * @typedef MeterProperties * @type {Object} * @property {number} rateUnit The rate unit. Defaults to 1000 (1 sec). * @property {number} tickInterval The interval in which the averages are updated. Defaults to 5000 (5 sec). * @property {boolean} keepAlive Optional flag to unref the associated timer. Defaults to `false`. * @example * const meter = new Meter({ rateUnit: 1000, tickInterval: 5000}) */ /** * The data returned from Meter::toJSON() * @interface MeterData * @typedef MeterData * @typedef {object} * @property {number} mean The average rate since the meter was started. * @property {number} count The total of all values added to the meter. * @property {number} currentRate The rate of the meter since the last toJSON() call. * @property {number} 1MinuteRate The rate of the meter biased towards the last 1 minute. * @property {number} 5MinuteRate The rate of the meter biased towards the last 5 minutes. * @property {number} 15MinuteRate The rate of the meter biased towards the last 15 minutes. */