You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gristlabs_grist-core/app/common/MetricCollector.js

102 lines
3.7 KiB

const _ = require('underscore');
const metricConfig = require('./metricConfig');
const metricTools = require('./metricTools');
const gutil = require('app/common/gutil');
/**
* Base class for metrics collection used by both the server metrics collector, ServerMetrics.js,
* and the client metrics collector, ClientMetrics.js. Should not be instantiated.
* Establishes interval attempts to push metrics to the server on creation.
*/
function MetricsCollector() {
this.startTime = metricTools.getBucketStartTime(Date.now());
this.readyToExport = [];
// used (as a protected member) by the derived ServerMetrics class.
this._collect = setTimeout(() => this.scheduleBucketPreparation(), metricTools.getDeltaMs(Date.now()));
}
// Should return a map from metric names (as entered in metricConfig.js) to their metricTools.
MetricsCollector.prototype.getMetrics = function() {
throw new Error("Not implemented");
};
// Should return a promise that is resolved when the metrics have been pushed.
MetricsCollector.prototype.pushMetrics = function() {
throw new Error("Not implemented");
};
// Should return a bucket of metric data, formatted for either the client or server.
MetricsCollector.prototype.createBucket = function(bucketStart) {
throw new Error("Not implemented");
};
// Takes a list of metrics specifications and creates an object mapping metric names to
// a new instance of the metric gathering tool matching that metric's type.
MetricsCollector.prototype.initMetricTools = function(metricsList) {
var metrics = {};
metricsList.forEach(metricInfo => {
metrics[metricInfo.name] = new metricTools[metricInfo.type](metricInfo.name);
});
return metrics;
};
// Called each push interval.
MetricsCollector.prototype.attemptPush = function() {
this.pushMetrics(this.readyToExport);
this.readyToExport = [];
};
// Pushes bucket to the end of the readyToExport queue. Should be called sequentially, since it
// handles deletion of buckets older than the export memory limit.
MetricsCollector.prototype.queueBucket = function(bucket) {
// If readyToExport is at maximum length, delete the oldest element
this.readyToExport.push(bucket);
var length = this.readyToExport.length;
if (length > metricConfig.MAX_PENDING_BUCKETS) {
this.readyToExport.splice(0, length - metricConfig.MAX_PENDING_BUCKETS);
}
};
MetricsCollector.prototype.scheduleBucketPreparation = function() {
this.prepareCompletedBuckets(Date.now());
this._collect = setTimeout(() => this.scheduleBucketPreparation(), metricTools.getDeltaMs(Date.now()));
};
/**
* Checks if each bucket since the last update is completed and for each one adds all data and
* pushes it to the export ready array.
*/
MetricsCollector.prototype.prepareCompletedBuckets = function(now) {
var bucketStart = metricTools.getBucketStartTime(now);
while (bucketStart > this.startTime) {
this.queueBucket(this.createBucket(this.startTime));
this.startTime += metricConfig.BUCKET_SIZE;
}
};
/**
* Collects primitive metrics tools into a list.
*/
MetricsCollector.prototype.collectPrimitiveMetrics = function() {
var metricTools = [];
_.forEach(this.getMetrics(), metricTool => {
gutil.arrayExtend(metricTools, metricTool.getPrimitiveMetrics());
});
return metricTools;
};
/**
* Loops through metric tools for a chosen bucket and performs the provided callback on each.
* Resets each tool after the callback is performed.
* @param {Number} bucketStart - The desired bucket's start time in milliseconds
* @param {Function} callback - The callback to perform on each metric tool.
*/
MetricsCollector.prototype.forEachBucketMetric = function(bucketEnd, callback) {
this.collectPrimitiveMetrics().forEach(tool => {
callback(tool);
tool.reset(bucketEnd);
});
};
module.exports = MetricsCollector;