Source: measured-core/lib/metrics/Timer.js

const { MetricTypes } = require('./Metric');
const Histogram = require('./Histogram');
const Meter = require('./Meter');
const Stopwatch = require('../util/Stopwatch');

/**
 *
 * Timers are a combination of Meters and Histograms. They measure the rate as well as distribution of scalar events.
 * <p>
 * Since they are frequently used for tracking how long certain things take, they expose an API for that: See example 1.
 * <p>
 * But you can also use them as generic histograms that also track the rate of events: See example 2.
 *
 * @example
 * var Measured = require('measured')
 * var timer = new Measured.Timer();
 * http.createServer(function(req, res) {
 *     var stopwatch = timer.start();
 *     req.on('end', function() {
 *         stopwatch.end();
 *     });
 * });
 *
 *
 * @example
 * var Measured = require('measured')
 * var timer = new Measured.Timer();
 * http.createServer(function(req, res) {
 *    if (req.headers['content-length']) {
 *        timer.update(parseInt(req.headers['content-length'], 10));
 *    }
 * });
 *
 * @implements {Metric}
 */
class Timer {
  /**
   * @param {TimerProperties} [properties] See {@link TimerProperties}.
   */
  constructor(properties) {
    properties = properties || {};

    this._meter = properties.meter || new Meter({});
    this._histogram = properties.histogram || new Histogram({});
    this._getTime = properties.getTime;
    this._keepAlive = !!properties.keepAlive;

    if (!properties.keepAlive) {
      this.unref();
    }
  }

  /**
   * @return {Stopwatch} Returns a Stopwatch that has been started.
   */
  start() {
    const self = this;
    const watch = new Stopwatch({ getTime: this._getTime });

    watch.once('end', elapsed => {
      self.update(elapsed);
    });

    return watch;
  }

  /**
   * Updates the internal histogram with value and marks one event on the internal meter.
   * @param {number} value
   */
  update(value) {
    this._meter.mark();
    this._histogram.update(value);
  }

  /**
   * Resets all values. Timers initialized with custom options will be reset to the default settings.
   */
  reset() {
    this._meter.reset();
    this._histogram.reset();
  }

  end() {
    this._meter.end();
  }

  /**
   * Refs the backing timer again. Idempotent.
   */
  ref() {
    this._meter.ref();
  }

  /**
   * Unrefs the backing timer. The meter will not keep the event loop alive. Idempotent.
   */
  unref() {
    this._meter.unref();
  }

  /**
   * toJSON output:
   *
   * <li> meter: See <a href="#meter">Meter</a>#toJSON output docs above.</li>
   * <li> histogram: See <a href="#histogram">Histogram</a>#toJSON output docs above.</a></li>
   *
   * @return {any}
   */
  toJSON() {
    return {
      meter: this._meter.toJSON(),
      histogram: this._histogram.toJSON()
    };
  }

  /**
   * The type of the Metric Impl. {@link MetricTypes}.
   * @return {string} The type of the Metric Impl.
   */
  getType() {
    return MetricTypes.TIMER;
  }
}

module.exports = Timer;

/**
 * @interface TimerProperties
 * @typedef TimerProperties
 * @type {Object}
 * @property {Meter} meter The internal meter to use. Defaults to a new {@link Meter}.
 * @property {Histogram} histogram The internal histogram to use. Defaults to a new {@link Histogram}.
 * @property {function} getTime optional function override for supplying time to the {@link Stopwatch}
 * @property {boolean} keepAlive Optional flag to unref the associated timer. Defaults to `false`.
 */