Skip to content

Commit

Permalink
feat: now CLIENT_METRICS event will be emitted with new structure (#7210
Browse files Browse the repository at this point in the history
)

1. CLIENT_METRICS event will be emitted with new structure
2. CLIENT_METRICS event will be emitted from bulkMetrics endpoint
  • Loading branch information
sjaanus authored May 31, 2024
1 parent 3c73ce9 commit d17ae37
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ test('can insert and read lifecycle stages', async () => {
);

function emitMetricsEvent(environment: string) {
eventBus.emit(CLIENT_METRICS, {
bucket: { toggles: { [featureName]: 'irrelevant' } },
environment,
});
eventBus.emit(CLIENT_METRICS, [
{
featureName,
environment,
},
]);
}
function reachedStage(feature: string, name: StageName) {
return new Promise((resolve) =>
Expand Down
24 changes: 16 additions & 8 deletions src/lib/features/feature-lifecycle/feature-lifecycle-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -95,13 +96,20 @@ 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;
await this.checkEnabled(() =>
this.featuresReceivedMetrics(features, 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),
);
}
}
},
);
Expand Down
41 changes: 22 additions & 19 deletions src/lib/features/feature-lifecycle/feature-lifecycle.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
environment: 'default',
});
{
featureName: 'non_existent_feature',
environment: 'default',
},
]);

// missing feature
eventBus.emit(CLIENT_METRICS, {
environment: 'default',
bucket: { toggles: {} },
});
eventBus.emit(CLIENT_METRICS, [
{
environment: 'default',
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',
},
environment: 'non-existent',
});
]);
await reachedStage('my_feature_a', 'live');
await expectFeatureStage('my_feature_a', 'live');
eventStore.emit(FEATURE_ARCHIVED, { featureName: 'my_feature_a' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/lib/features/metrics/instance/metrics.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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();
Expand Down
15 changes: 6 additions & 9 deletions src/lib/metrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
yes: 10,
no: 5,
},
},
eventBus.emit(CLIENT_METRICS, [
{
featureName: 'TestToggle',
yes: 10,
no: 5,
},
});
]);

const metrics = await prometheusRegister.metrics();
expect(metrics).toMatch(
Expand Down
19 changes: 10 additions & 9 deletions src/lib/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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() {}
Expand Down Expand Up @@ -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(':');
Expand Down

0 comments on commit d17ae37

Please sign in to comment.