Source: measured-reporting/lib/registries/DimensionAwareMetricsRegistry.js

const mapcap = require('mapcap');

/**
 * Simple registry that stores Metrics by name and dimensions.
 */
class DimensionAwareMetricsRegistry {
  /**
   * @param {DimensionAwareMetricsRegistryOptions} [options] Configurable options for the Dimension Aware Metrics Registry
   */
  constructor(options) {
    options = options || {};

    let metrics = new Map();
    if (options.metricLimit) {
      metrics = mapcap(metrics, options.metricLimit, options.lru);
    }

    this._metrics = metrics;
  }

  /**
   * Checks to see if a metric with the given name and dimensions is present.
   *
   * @param {string} name The metric name
   * @param {Dimensions} dimensions The dimensions for the metric
   * @returns {boolean} true if the metric with given dimensions is present
   */
  hasMetric(name, dimensions) {
    const key = this._generateStorageKey(name, dimensions);
    return this._metrics.has(key);
  }

  /**
   * Retrieves a metric with a given name and dimensions is present.
   *
   * @param {string} name The metric name
   * @param {Dimensions} dimensions The dimensions for the metric
   * @returns {Metric} a wrapper object around name, dimension and {@link Metric}
   */
  getMetric(name, dimensions) {
    const key = this._generateStorageKey(name, dimensions);
    return this._metrics.get(key).metricImpl;
  }

  /**
   * Retrieves a metric by the calculated key (name / dimension combo).
   *
   * @param {string} key The registered key for the given registered {@link MetricWrapper}
   * @returns {MetricWrapper} a wrapper object around name, dimension and {@link Metric}
   */
  getMetricWrapperByKey(key) {
    return this._metrics.get(key);
  }

  /**
   * Upserts a {@link Metric} in the internal storage map for a given name, dimension combo
   *
   * @param {string} name The metric name
   * @param {Metric} metric The {@link Metric} impl
   * @param {Dimensions} dimensions The dimensions for the metric
   * @return {string} The registry key for the metric, dimension combo
   */
  putMetric(name, metric, dimensions) {
    const key = this._generateStorageKey(name, dimensions);
    this._metrics.set(key, {
      name: name,
      metricImpl: metric,
      dimensions: dimensions || {}
    });
    return key;
  }

  /**
   * Returns an array of all keys of metrics stored in this registry.
   * @return {string[]} all keys of metrics stored in this registry.
   */
  allKeys() {
    return Array.from(this._metrics.keys());
  }

  /**
   * Generates a unique key off of the metric name and custom dimensions for internal use in the registry maps.
   *
   * @param {string} name The metric name
   * @param {Dimensions} dimensions The dimensions for the metric
   * @return {string} a unique key based off of the metric nae and dimensions
   * @private
   */
  _generateStorageKey(name, dimensions) {
    let key = name;
    if (dimensions) {
      Object.keys(dimensions)
        .sort()
        .forEach(dimensionKey => {
          key = `${key}-${dimensions[dimensionKey]}`;
        });
    }
    return key;
  }
}

module.exports = DimensionAwareMetricsRegistry;

/**
 * Configurable options for the Dimension Aware Metrics Registry
 *
 * @interface DimensionAwareMetricsRegistryOptions
 * @typedef DimensionAwareMetricsRegistryOptions
 * @property {Number} metricLimit the maximum number of metrics the registry may hold before dropping metrics
 * @property {Boolean} lru switch dropping strategy from "least recently added" to "least recently used"
 */