mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: now CLIENT_METRICS event will be emitted with new structure (#7210)
1. CLIENT_METRICS event will be emitted with new structure 2. CLIENT_METRICS event will be emitted from bulkMetrics endpoint
This commit is contained in:
parent
3c73ce9dd9
commit
d17ae37800
@ -33,10 +33,12 @@ test('can insert and read lifecycle stages', async () => {
|
||||
);
|
||||
|
||||
function emitMetricsEvent(environment: string) {
|
||||
eventBus.emit(CLIENT_METRICS, {
|
||||
bucket: { toggles: { [featureName]: 'irrelevant' } },
|
||||
eventBus.emit(CLIENT_METRICS, [
|
||||
{
|
||||
featureName,
|
||||
environment,
|
||||
});
|
||||
},
|
||||
]);
|
||||
}
|
||||
function reachedStage(feature: string, name: StageName) {
|
||||
return new Promise((resolve) =>
|
||||
|
@ -20,9 +20,10 @@ import type {
|
||||
import EventEmitter from 'events';
|
||||
import type { Logger } from '../../logger';
|
||||
import type EventService from '../events/event-service';
|
||||
import type { ValidatedClientMetrics } from '../metrics/shared/schema';
|
||||
import type { FeatureLifecycleCompletedSchema } from '../../openapi';
|
||||
import { calculateStageDurations } from './calculate-stage-durations';
|
||||
import type { IClientMetricsEnv } from '../metrics/client-metrics/client-metrics-store-v2-type';
|
||||
import groupBy from 'lodash.groupby';
|
||||
|
||||
export const STAGE_ENTERED = 'STAGE_ENTERED';
|
||||
|
||||
@ -95,14 +96,21 @@ export class FeatureLifecycleService extends EventEmitter {
|
||||
});
|
||||
this.eventBus.on(
|
||||
CLIENT_METRICS,
|
||||
async (event: ValidatedClientMetrics) => {
|
||||
if (event.environment) {
|
||||
const features = Object.keys(event.bucket.toggles);
|
||||
const environment = event.environment;
|
||||
async (events: IClientMetricsEnv[]) => {
|
||||
if (events.length > 0) {
|
||||
const groupedByEnvironment = groupBy(events, 'environment');
|
||||
|
||||
for (const [environment, metrics] of Object.entries(
|
||||
groupedByEnvironment,
|
||||
)) {
|
||||
const features = metrics.map(
|
||||
(metric) => metric.featureName,
|
||||
);
|
||||
await this.checkEnabled(() =>
|
||||
this.featuresReceivedMetrics(features, environment),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
this.eventStore.on(FEATURE_ARCHIVED, async (event) => {
|
||||
|
@ -121,29 +121,32 @@ test('should return lifecycle stages', async () => {
|
||||
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' });
|
||||
await reachedStage('my_feature_a', 'initial');
|
||||
await expectFeatureStage('my_feature_a', 'initial');
|
||||
eventBus.emit(CLIENT_METRICS, {
|
||||
bucket: {
|
||||
toggles: {
|
||||
my_feature_a: 'irrelevant',
|
||||
non_existent_feature: 'irrelevant',
|
||||
},
|
||||
},
|
||||
eventBus.emit(CLIENT_METRICS, [
|
||||
{
|
||||
featureName: 'my_feature_a',
|
||||
environment: 'default',
|
||||
});
|
||||
},
|
||||
{
|
||||
featureName: 'non_existent_feature',
|
||||
environment: 'default',
|
||||
},
|
||||
]);
|
||||
|
||||
// missing feature
|
||||
eventBus.emit(CLIENT_METRICS, {
|
||||
eventBus.emit(CLIENT_METRICS, [
|
||||
{
|
||||
environment: 'default',
|
||||
bucket: { toggles: {} },
|
||||
});
|
||||
yes: 0,
|
||||
no: 0,
|
||||
},
|
||||
]);
|
||||
// non existent env
|
||||
eventBus.emit(CLIENT_METRICS, {
|
||||
bucket: {
|
||||
toggles: {
|
||||
my_feature_a: 'irrelevant',
|
||||
},
|
||||
},
|
||||
eventBus.emit(CLIENT_METRICS, [
|
||||
{
|
||||
featureName: 'my_feature_a',
|
||||
environment: 'non-existent',
|
||||
});
|
||||
},
|
||||
]);
|
||||
await reachedStage('my_feature_a', 'live');
|
||||
await expectFeatureStage('my_feature_a', 'live');
|
||||
eventStore.emit(FEATURE_ARCHIVED, { featureName: 'my_feature_a' });
|
||||
|
@ -171,7 +171,7 @@ export default class ClientMetricsServiceV2 {
|
||||
);
|
||||
await this.registerBulkMetrics(clientMetrics);
|
||||
|
||||
this.config.eventBus.emit(CLIENT_METRICS, value);
|
||||
this.config.eventBus.emit(CLIENT_METRICS, clientMetrics);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import type { Response } from 'express';
|
||||
import Controller from '../../../routes/controller';
|
||||
import type {
|
||||
IFlagResolver,
|
||||
IUnleashConfig,
|
||||
IUnleashServices,
|
||||
import {
|
||||
CLIENT_METRICS,
|
||||
type IFlagResolver,
|
||||
type IUnleashConfig,
|
||||
type IUnleashServices,
|
||||
} from '../../../types';
|
||||
import type ClientInstanceService from './instance-service';
|
||||
import type { Logger } from '../../../logger';
|
||||
@ -161,8 +162,10 @@ export default class ClientMetricsController extends Controller {
|
||||
promises.push(
|
||||
this.metricsV2.registerBulkMetrics(filteredData),
|
||||
);
|
||||
this.config.eventBus.emit(CLIENT_METRICS, data);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
res.status(202).end();
|
||||
} catch (e) {
|
||||
res.status(400).end();
|
||||
|
@ -162,16 +162,13 @@ test('should set environmentType when toggle is flipped', async () => {
|
||||
});
|
||||
|
||||
test('should collect metrics for client metric reports', async () => {
|
||||
eventBus.emit(CLIENT_METRICS, {
|
||||
bucket: {
|
||||
toggles: {
|
||||
TestToggle: {
|
||||
eventBus.emit(CLIENT_METRICS, [
|
||||
{
|
||||
featureName: 'TestToggle',
|
||||
yes: 10,
|
||||
no: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
]);
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(
|
||||
|
@ -24,7 +24,6 @@ import type { IUnleashConfig } from './types/option';
|
||||
import type { ISettingStore, IUnleashStores } from './types/stores';
|
||||
import { hoursToMilliseconds, minutesToMilliseconds } from 'date-fns';
|
||||
import type { InstanceStatsService } from './features/instance-stats/instance-stats-service';
|
||||
import type { ValidatedClientMetrics } from './features/metrics/shared/schema';
|
||||
import type { IEnvironment } from './types';
|
||||
import {
|
||||
createCounter,
|
||||
@ -33,6 +32,7 @@ import {
|
||||
createHistogram,
|
||||
} from './util/metrics';
|
||||
import type { SchedulerService } from './services';
|
||||
import type { IClientMetricsEnv } from './features/metrics/client-metrics/client-metrics-store-v2-type';
|
||||
|
||||
export default class MetricsMonitor {
|
||||
constructor() {}
|
||||
@ -617,30 +617,31 @@ export default class MetricsMonitor {
|
||||
});
|
||||
|
||||
const logger = config.getLogger('metrics.ts');
|
||||
eventBus.on(CLIENT_METRICS, (m: ValidatedClientMetrics) => {
|
||||
eventBus.on(CLIENT_METRICS, (metrics: IClientMetricsEnv[]) => {
|
||||
try {
|
||||
for (const entry of Object.entries(m.bucket.toggles)) {
|
||||
for (const metric of metrics) {
|
||||
featureFlagUsageTotal.increment(
|
||||
{
|
||||
toggle: entry[0],
|
||||
toggle: metric.featureName,
|
||||
active: 'true',
|
||||
appName: m.appName,
|
||||
appName: metric.appName,
|
||||
},
|
||||
entry[1].yes,
|
||||
metric.yes,
|
||||
);
|
||||
featureFlagUsageTotal.increment(
|
||||
{
|
||||
toggle: entry[0],
|
||||
toggle: metric.featureName,
|
||||
active: 'false',
|
||||
appName: m.appName,
|
||||
appName: metric.appName,
|
||||
},
|
||||
entry[1].no,
|
||||
metric.no,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('Metrics registration failed', e);
|
||||
}
|
||||
});
|
||||
|
||||
eventStore.on(CLIENT_REGISTER, (m) => {
|
||||
if (m.sdkVersion && m.sdkVersion.indexOf(':') > -1) {
|
||||
const [sdkName, sdkVersion] = m.sdkVersion.split(':');
|
||||
|
Loading…
Reference in New Issue
Block a user