UNPKG

dl

Version:

DreamLab Libs

270 lines (203 loc) 7.61 kB
var core = require('core'); var AbstractReporter = require('./AbstractReporter.js').AbstractReporter; var CounterMetric = require('./metrics/CounterMetric.js').CounterMetric; var GaugeMetric = require('./metrics/GaugeMetric.js').GaugeMetric; var HistogramMetric = require('./metrics/HistogramMetric.js').HistogramMetric; var OpalLoader = require('../opal/OpalLoader.js').OpalLoader; var OpalRequest = require('../opal/OpalRequest.js').OpalRequest; var Types = core.common.Types; var SECOND = 1000; /* * How it works ? * - start collecting metrics at 0:45 and collect for one minute (collect 0:45 - 1:45) * - every second try to send outdated and current metrics - we have 15 seconds to do it on time * - for current timestamp do not send metrics often than once every 15 seconds */ var MaasReporter = function () { AbstractReporter.apply(this, arguments); this._metrics = {}; this._metricsCount = {}; this._endpoint = MaasReporter.DEFAULTS.ENDPOINT; this._maxSendAtOnce = MaasReporter.DEFAULTS.MAX_SEND_AT_ONCE; this._maxMetricsCount = MaasReporter.DEFAULTS.MAX_METRICS; this._maxRetries = MaasReporter.DEFAULTS.MAX_RETRIES; this._sendInterval = MaasReporter.DEFAULTS.SEND_INTERVAL; this._lastSend = {}; this._started = false; }; MaasReporter.prototype = Object.create(AbstractReporter.prototype); MaasReporter.prototype._send = function () { var currentMinute = this._getMinute(); var allowedMinute = this._getMinute(Date.now()); if (!this._lastSend[currentMinute]) { this._lastSend[currentMinute] = Date.now(); } var minutes = Object.keys(this._metrics); for (var i = 0, il = minutes.length; i < il; i++) { var minute = parseInt(minutes[i]); if (minute === currentMinute && ((Date.now() - this._lastSend[minute]) < this._sendInterval)) { continue; } if (minute === allowedMinute) { this._reportStats('maas.send.in-time'); } else { this._reportStats('maas.send.late'); } var metrics = this._metrics[minute]; var keysToSend = Object.keys(metrics); while (keysToSend.length > 0) { var keys = keysToSend.splice(0, this._maxSendAtOnce); var metricsToSend = {}; for (var j = 0, jl = keys.length; j < jl; j++) { var dump = metrics[keys[j]].dump(); for (var k = 0, jk = dump.length; k < jk; k++) { metricsToSend[dump[k].key] = dump[k].value; } } this._sendMetrics(metricsToSend); } this._lastSend[minute] = Date.now(); delete this._metrics[minute]; delete this._metricsCount[minute]; } }; MaasReporter.prototype._sendMetrics = function (metrics, retry) { var that = this; retry = retry || 0; if (retry > this._maxRetries) { var metricsCount = Object.keys(metrics).length; console.error('MaasReporter: send retry limit reached - drop', metricsCount, 'metrics'); this._reportStats('maas.metrics.dropped', metricsCount); return; } var req = new OpalRequest({ 'url': this._endpoint, 'params': { 'monitoring_key': this._config.key, 'metrics': metrics }, 'method': 'submit' }); var loader = new OpalLoader(req); loader.addEventListener(OpalLoader.Event.JSON_RESPONSE, function (e) { if (e.data.getBody().isError()) { console.error('MaasReporter: failed to report metrics to MaaS (%s): %s', retry, e.data.getBody().getError()); that._reportStats('maas.send.error'); that._sendMetrics(metrics, retry + 1); } }, this); loader.addEventListener(OpalLoader.Event.HTTP_RESPONSE, function (e) { console.error('MaasReporter: failed to report metrics to MaaS (%s), sc: %s', retry, e.data.getStatusCode()); that._reportStats('maas.send.error'); that._sendMetrics(metrics, retry + 1); }, this); loader.addEventListener(OpalLoader.Event.ERROR, function (e) { console.error('MaasReporter: failed to report metrics to MaaS (%s), code: %s, err: %s', retry, e.code, e.message); that._reportStats('maas.send.error'); that._sendMetrics(metrics, retry + 1); }, this); loader.setLogStatus(false); loader.setTimeout(4 * SECOND); loader.load(); }; MaasReporter.prototype._getMinute = function (timestamp) { if (!timestamp) { timestamp = Date.now() + 15 * SECOND; } var date = new Date(timestamp); date.setMilliseconds(0); date.setSeconds(0); return date.getMinutes(); }; MaasReporter.prototype._getMetric = function (name) { var minute = this._getMinute(); if (!this._metrics.hasOwnProperty(minute)) { return null; } if (!this._metrics[minute].hasOwnProperty(name)) { return null; } return this._metrics[minute][name]; }; MaasReporter.prototype._createMetric = function (name, value) { var minute = this._getMinute(); if (!this._metrics.hasOwnProperty(minute)) { this._metrics[minute] = {}; } if (!this._metricsCount.hasOwnProperty(minute)) { this._metricsCount[minute] = 0; } if (!this._started) { this._start(); } var metrics = this._metrics[minute]; if (!metrics.hasOwnProperty(name)) { if (this._metricsCount[minute] >= this._maxMetricsCount) { this._reportStats('maas.metrics.dropped'); return; } this._metricsCount[minute]++; this._reportStats('maas.metrics.collected'); } metrics[name] = value; }; MaasReporter.prototype._start = function () { var that = this; setInterval(function () { that._send(); }, SECOND); this._started = true; }; MaasReporter.prototype.setConfig = function () { AbstractReporter.prototype.setConfig.apply(this, arguments); if (this._config.hasOwnProperty('endpoint')) { this._endpoint = this._config['endpoint']; } if (this._config.hasOwnProperty('maxSendAtOnce')) { this._maxSendAtOnce = this._config['maxSendAtOnce']; } if (this._config.hasOwnProperty('maxMetrics')) { this._maxMetricsCount = this._config['maxMetrics']; } if (this._config.hasOwnProperty('sendInterval')) { this._sendInterval = this._config['sendInterval']; } if (this._config.hasOwnProperty('maxRetries')) { this._maxRetries = this._config['maxRetries']; } return this; }; MaasReporter.prototype.gauge = function (name, value, buckets) { if (!Types.isNumber(value)) { this._reportStats('maas.metrics.incorrect'); return this; } var metric = this._getMetric(name); if (!metric) { if (buckets) { this._createMetric(name, new HistogramMetric(name, buckets).update(value)); } else { this._createMetric(name, new GaugeMetric(name).update(value)); } return this; } metric.update(value); return this; }; MaasReporter.prototype.counter = function (name) { var metric = this._getMetric(name); if (!metric) { this._createMetric(name, new CounterMetric(name).update()); return this; } metric.update(); return this; }; MaasReporter.DEFAULTS = { ENDPOINT: 'llogd2-receiver.monitoring.onetapi.pl', SEND_INTERVAL: 15 * SECOND, MAX_SEND_AT_ONCE: 100, MAX_METRICS: 10000, MAX_RETRIES: 3 }; exports.MaasReporter = MaasReporter;