1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-10-18 20:09:08 +02:00
unleash.unleash/lib/services/client-metrics/index.js
Ivar Conradi Østhus cdfba8f7b1 feat: Adds last-seen dat on toggles
When an application updates metrics for a toggle we now
stores the timestamp on the toggle when it was last seen
used by an application. This will make it much easier to
detect toggles not in use anymore.

closes #642
2020-12-22 11:05:00 +01:00

147 lines
4.1 KiB
JavaScript

/* eslint-disable no-param-reassign */
'use strict';
const Projection = require('./projection.js');
const TTLList = require('./ttl-list.js');
module.exports = class ClientMetricsService {
constructor({ clientMetricsStore }) {
this.globalCount = 0;
this.apps = {};
this.lastHourProjection = new Projection();
this.lastMinuteProjection = new Projection();
this.lastHourList = new TTLList({
interval: 10000,
});
this.lastMinuteList = new TTLList({
interval: 10000,
expireType: 'minutes',
expireAmount: 1,
});
this.lastHourList.on('expire', toggles => {
Object.keys(toggles).forEach(toggleName => {
this.lastHourProjection.substract(
toggleName,
this.createCountObject(toggles[toggleName]),
);
});
});
this.lastMinuteList.on('expire', toggles => {
Object.keys(toggles).forEach(toggleName => {
this.lastMinuteProjection.substract(
toggleName,
this.createCountObject(toggles[toggleName]),
);
});
});
clientMetricsStore.on('metrics', m => this.addPayload(m));
}
getAppsWithToggles() {
const apps = [];
Object.keys(this.apps).forEach(appName => {
const seenToggles = Object.keys(this.apps[appName].seenToggles);
const metricsCount = this.apps[appName].count;
apps.push({ appName, seenToggles, metricsCount });
});
return apps;
}
getSeenTogglesByAppName(appName) {
return this.apps[appName]
? Object.keys(this.apps[appName].seenToggles)
: [];
}
getSeenAppsPerToggle() {
const toggles = {};
Object.keys(this.apps).forEach(appName => {
Object.keys(this.apps[appName].seenToggles).forEach(
seenToggleName => {
if (!toggles[seenToggleName]) {
toggles[seenToggleName] = [];
}
toggles[seenToggleName].push({ appName });
},
);
});
return toggles;
}
getTogglesMetrics() {
return {
lastHour: this.lastHourProjection.getProjection(),
lastMinute: this.lastMinuteProjection.getProjection(),
};
}
addPayload(data) {
const { appName, bucket } = data;
const app = this.getApp(appName);
this.addBucket(app, bucket);
}
getApp(appName) {
this.apps[appName] = this.apps[appName] || {
seenToggles: {},
count: 0,
};
return this.apps[appName];
}
createCountObject(entry) {
let yes = typeof entry.yes === 'number' ? entry.yes : 0;
let no = typeof entry.no === 'number' ? entry.no : 0;
if (entry.variants) {
Object.entries(entry.variants).forEach(([key, value]) => {
if (key === 'disabled') {
no += value;
} else {
yes += value;
}
});
}
return { yes, no };
}
addBucket(app, bucket) {
let count = 0;
// TODO stop should be createdAt
const { stop, toggles } = bucket;
const toggleNames = Object.keys(toggles);
toggleNames.forEach(n => {
const countObj = this.createCountObject(toggles[n]);
this.lastHourProjection.add(n, countObj);
this.lastMinuteProjection.add(n, countObj);
count += countObj.yes + countObj.no;
});
this.lastHourList.add(toggles, stop);
this.lastMinuteList.add(toggles, stop);
this.globalCount += count;
app.count += count;
this.addSeenToggles(app, toggleNames);
}
addSeenToggles(app, toggleNames) {
toggleNames.forEach(t => {
app.seenToggles[t] = true;
});
}
destroy() {
this.lastHourList.destroy();
this.lastMinuteList.destroy();
}
};