@connectedcars/logutil
Version:
Simple log formatting for Node
241 lines (240 loc) • 7.72 kB
JavaScript
;
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