File: //usr/share/opensearch-dashboards/node_modules/measured-core/lib/Collection.js
const Optional = require('optional-js');
const Counter = require('./metrics/Counter');
const Gauge = require('./metrics/Gauge');
const SettableGauge = require('./metrics/SettableGauge');
const CachedGauge = require('./metrics/CachedGauge');
const Histogram = require('./metrics/Histogram');
const Meter = require('./metrics/Meter');
const Timer = require('./metrics/Timer');
const { MetricTypes } = require('./metrics/Metric');
/**
* A Simple collection that stores names and a {@link Metric} instances with a few convenience methods for
* creating / registering and then gathering all data the registered metrics.
* @example
* var { Collection } = require('measured');
* const collection = new Collection('node-process-metrics');
* const gauge = collection.gauge('node.process.heap_used', () => {
* return process.memoryUsage().heapUsed;
* });
*/
class Collection {
/**
* Creates a named collection of metrics
* @param {string} [name] The name to use for this collection.
*/
constructor(name) {
this.name = name;
/**
* internal map of metric name to {@link Metric}
* @type {Object.<string, Metric>}
* @private
*/
this._metrics = {};
}
/**
* register a metric that was created outside the provided convenience methods of this collection
* @param name The metric name
* @param metric The {@link Metric} implementation
* @example
* var { Collection, Gauge } = require('measured');
* const collection = new Collection('node-process-metrics');
* const gauge = new Gauge(() => {
* return process.memoryUsage().heapUsed;
* });
* collection.register('node.process.heap_used', gauge);
*/
register(name, metric) {
this._metrics[name] = metric;
}
/**
* Fetches the data/values from all registered metrics
* @return {Object} The combined JSON object
*/
toJSON() {
const json = {};
Object.keys(this._metrics).forEach(metric => {
if (Object.prototype.hasOwnProperty.call(this._metrics, metric)) {
json[metric] = this._metrics[metric].toJSON();
}
});
if (!this.name) {
return json;
}
const wrapper = {};
wrapper[this.name] = json;
return wrapper;
}
/**
* Gets or creates and registers a {@link Gauge}
* @param {string} name The metric name
* @param {function} readFn See {@link Gauge}
* @return {Gauge}
*/
gauge(name, readFn) {
this._validateName(name);
let gauge;
this._getMetricForNameAndType(name, MetricTypes.GAUGE).ifPresentOrElse(
registeredMetric => {
gauge = registeredMetric;
},
() => {
gauge = new Gauge(readFn);
this.register(name, gauge);
}
);
return gauge;
}
/**
* Gets or creates and registers a {@link Counter}
* @param {string} name The metric name
* @param {CounterProperties} [properties] See {@link CounterProperties}
* @return {Counter}
*/
counter(name, properties) {
this._validateName(name);
let counter;
this._getMetricForNameAndType(name, MetricTypes.COUNTER).ifPresentOrElse(
registeredMetric => {
counter = registeredMetric;
},
() => {
counter = new Counter(properties);
this.register(name, counter);
}
);
return counter;
}
/**
* Gets or creates and registers a {@link Histogram}
* @param {string} name The metric name
* @param {HistogramProperties} [properties] See {@link HistogramProperties}
* @return {Histogram}
*/
histogram(name, properties) {
this._validateName(name);
let histogram;
this._getMetricForNameAndType(name, MetricTypes.HISTOGRAM).ifPresentOrElse(
registeredMetric => {
histogram = registeredMetric;
},
() => {
histogram = new Histogram(properties);
this.register(name, histogram);
}
);
return histogram;
}
/**
* Gets or creates and registers a {@link Timer}
* @param {string} name The metric name
* @param {TimerProperties} [properties] See {@link TimerProperties}
* @return {Timer}
*/
timer(name, properties) {
this._validateName(name);
let timer;
this._getMetricForNameAndType(name, MetricTypes.TIMER).ifPresentOrElse(
registeredMetric => {
timer = registeredMetric;
},
() => {
timer = new Timer(properties);
this.register(name, timer);
}
);
return timer;
}
/**
* Gets or creates and registers a {@link Meter}
* @param {string} name The metric name
* @param {MeterProperties} [properties] See {@link MeterProperties}
* @return {Meter}
*/
meter(name, properties) {
this._validateName(name);
let meter;
this._getMetricForNameAndType(name, MetricTypes.METER).ifPresentOrElse(
registeredMetric => {
meter = registeredMetric;
},
() => {
meter = new Meter(properties);
this.register(name, meter);
}
);
return meter;
}
/**
* Gets or creates and registers a {@link SettableGauge}
* @param {string} name The metric name
* @param {SettableGaugeProperties} [properties] See {@link SettableGaugeProperties}
* @return {SettableGauge}
*/
settableGauge(name, properties) {
this._validateName(name);
let settableGauge;
this._getMetricForNameAndType(name, MetricTypes.GAUGE).ifPresentOrElse(
registeredMetric => {
settableGauge = registeredMetric;
},
() => {
settableGauge = new SettableGauge(properties);
this.register(name, settableGauge);
}
);
return settableGauge;
}
/**
* Gets or creates and registers a {@link SettableGauge}
* @param {string} name The metric name
* @param {function} valueProducingPromiseCallback A function that returns a promise than when
* resolved supplies the value that should be cached in this gauge.
* @param {number} updateIntervalInSeconds How often the cached gauge should update it's value.
* @return {CachedGauge}
*/
cachedGauge(name, valueProducingPromiseCallback, updateIntervalInSeconds) {
this._validateName(name);
let cachedGauge;
this._getMetricForNameAndType(name, MetricTypes.GAUGE).ifPresentOrElse(
registeredMetric => {
cachedGauge = registeredMetric;
},
() => {
cachedGauge = new CachedGauge(valueProducingPromiseCallback, updateIntervalInSeconds);
this.register(name, cachedGauge);
}
);
return cachedGauge;
}
/**
* Checks the registry for a metric with a given name and type, if it exists in the registry as a
* different type an error is thrown.
* @param {string} name The metric name
* @param {string} requestedType The metric type
* @return {Optional<Metric>}
* @private
*/
_getMetricForNameAndType(name, requestedType) {
if (this._metrics[name]) {
const metric = this._metrics[name];
const actualType = metric.getType();
if (requestedType !== actualType) {
throw new Error(
`You requested a metric of type: ${requestedType} with name: ${name}, but it exists in the registry as type: ${actualType}`
);
}
return Optional.of(metric);
}
return Optional.empty();
}
/**
* Validates that the provided name is valid.
*
* @param name The provided metric name param.
* @private
*/
_validateName(name) {
if (!name || typeof name !== 'string') {
throw new Error('You must supply a metric name');
}
}
/**
* Calls end on all metrics in the registry that support end()
*/
end() {
const metrics = this._metrics;
Object.keys(metrics).forEach(name => {
const metric = metrics[name];
if (metric.end) {
metric.end();
}
});
}
}
module.exports = Collection;