UNPKG

@connectedcars/logutil

Version:
241 lines (240 loc) 7.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MetricType = exports.MetricRegistry = void 0; exports.clearMetricRegistry = clearMetricRegistry; exports.getMetricRegistry = getMetricRegistry; var _events = require("events"); var _error = require("./error"); var _statistic = require("./statistic"); function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } let MetricType; exports.MetricType = MetricType; (function (MetricType) { MetricType["GAUGE"] = "GAUGE"; MetricType["CUMULATIVE"] = "CUMULATIVE"; })(MetricType || (exports.MetricType = MetricType = {})); function isGaugeMetric(metric) { return metric.type === MetricType.GAUGE; } function isCumulativeMetric(metric) { return metric.type === MetricType.CUMULATIVE; } class MetricRegistry extends _events.EventEmitter { constructor() { super(...arguments); _defineProperty(this, "metrics", {}); } logMetrics() { const metrics = this.getMetrics(); const result = {}; for (const metric of metrics) { if (Date.now() - metric.endTime > 24 * 60 * 60 * 1000) { // Skip data points more than 24 hours old continue; } const formattedMetric = this.convertTimestampToIsoString(metric); if (result[metric.name]) { result[metric.name].push(formattedMetric); } else { result[metric.name] = [formattedMetric]; } } for (const [metricName, metrics] of Object.entries(result)) { // Emit each metric name as separate event this.emit(metricName, { metrics }); } for (const metrics of Object.values(result)) { while (metrics.length > 0) { const metricsBatch = metrics.splice(0, 250); (0, _statistic.statistic)('Metric dump', { metrics: metricsBatch }); } } // After dumping and emmiting metrics, remove all existing metrics // We don't miss out on any metrics here, because this code is purely synchronous this.metrics = {}; } getMetrics() { const result = []; for (const key of Object.keys(this.metrics)) { const metric = { ...this.metrics[key] }; metric.endTime = metric.endTime ? metric.endTime : Date.now(); if (isGaugeMetric(metric) && metric.reducerFn) { metric.value = metric.reducerFn(metric.value); } result.push(metric); } return result; } gauge(name, value, labels) { let reducerFn = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; try { this.formatLabels(labels); const key = this.createKey(name, labels); let metric = this.metrics[key]; if (!metric) { metric = this.metrics[key] = { name: name, type: MetricType.GAUGE, value: reducerFn ? [value] : value, labels: labels }; if (reducerFn) { metric.reducerFn = reducerFn; } } else { if (!isGaugeMetric(metric)) { (0, _error.error)('Cannot add gauge with same name as existing cumulative', { name, value, labels }); } if (isGaugeMetric(metric) && metric.reducerFn) { if (!reducerFn) { (0, _error.error)('Gauge with reducer called without reducer', { name }); } ; metric.value.push(value); } else { if (reducerFn) { (0, _error.error)('Gauge without reducer called with reducer', { name }); } metric.value = value; metric.endTime = Date.now(); } } } catch (e) { (0, _error.error)('Failed logging metric', { message: e.message, stack: e.stack }); } } cumulative(name, value, labels) { try { this.formatLabels(labels); const key = this.createKey(name, labels); if (!this.metrics[key]) { this.metrics[key] = { name: name, type: MetricType.CUMULATIVE, value: 0, labels: labels, startTime: Date.now() }; } const metric = this.metrics[key]; if (!isCumulativeMetric(metric)) { (0, _error.error)('Cannot add cumulative with same name as existing gauge', { name, value, labels }); } if (typeof metric.value === 'number') { metric.value += value; } } catch (e) { (0, _error.error)('Failed logging metric', { message: e.message, stack: e.stack }); } } getMetric(name) { const metrics = Object.values(this.metrics).filter(m => m.name === name); if (metrics.length === 0) { return []; } const res = []; for (const metric of metrics) { const value = isGaugeMetric(metric) && metric.reducerFn ? metric.reducerFn(metric.value) : metric.value; const filteredMetric = { name: metric.name, type: metric.type, value, labels: metric.labels, endTime: metric.endTime ? metric.endTime : Date.now() }; if (filteredMetric.type === MetricType.CUMULATIVE) { filteredMetric.startTime = metric.startTime; } res.push(filteredMetric); } return res; } clearMetric(name) { const metrics = Object.keys(this.metrics).filter(key => this.metrics[key].name === name); for (const metricName of metrics) { delete this.metrics[metricName]; } } getMetricNames() { return Object.keys(this.metrics); } convertTimestampToIsoString(metric) { if (metric.startTime) { metric.startTime = new Date(metric.startTime).toISOString(); } metric.endTime = new Date(metric.endTime).toISOString(); return metric; } createKey(name, labels) { if (labels) { const labelKey = Object.keys(labels).map(k => [k, labels[k]].join(':')).join(':'); return [name, labelKey].join('-'); } return name; } formatLabels(labels) { if (!labels) { return; } for (const [key, value] of Object.entries(labels)) { labels[key] = String(value); } } } exports.MetricRegistry = MetricRegistry; let metricRegistry = null; let scrapeInterval; function logMetrics(delay) { if (!metricRegistry) { metricRegistry = new MetricRegistry(); } if (delay > -1) { metricRegistry.logMetrics(); scrapeInterval = setTimeout(() => logMetrics(delay), delay); } } function getMetricRegistry() { let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 60 * 1000; if (!metricRegistry) { metricRegistry = new MetricRegistry(); logMetrics(delay); } return metricRegistry; } function clearMetricRegistry() { var _metricRegistry; if (scrapeInterval) { clearTimeout(scrapeInterval); } (_metricRegistry = metricRegistry) === null || _metricRegistry === void 0 ? void 0 : _metricRegistry.removeAllListeners(); metricRegistry = null; } //# sourceMappingURL=metric.js.map