Skip to content

Commit 293609c

Browse files
committed
chore: split report and filter flags, add gauge
1 parent 8306cf6 commit 293609c

File tree

6 files changed

+63
-22
lines changed

6 files changed

+63
-22
lines changed

src/lib/features/metrics/client-metrics/metrics-service-v2.ts

+29-17
Original file line numberDiff line numberDiff line change
@@ -125,37 +125,49 @@ export default class ClientMetricsServiceV2 {
125125
validatedToggleNames: string[];
126126
unknownToggleNames: string[];
127127
}> {
128+
let toggleNamesToValidate: string[] = toggleNames;
128129
let unknownToggleNames: string[] = [];
129130

130-
if (this.flagResolver.isEnabled('filterExistingFlagNames')) {
131+
const shouldFilter = this.flagResolver.isEnabled(
132+
'filterExistingFlagNames',
133+
);
134+
const shouldReport = this.flagResolver.isEnabled('reportUnknownFlags');
135+
136+
if (shouldFilter || shouldReport) {
131137
try {
132-
const validNames = await this.cachedFeatureNames();
138+
const existingFlags = await this.cachedFeatureNames();
133139

134140
const existingNames = toggleNames.filter((name) =>
135-
validNames.includes(name),
141+
existingFlags.includes(name),
142+
);
143+
const nonExistingNames = toggleNames.filter(
144+
(name) => !existingFlags.includes(name),
136145
);
137-
if (this.flagResolver.isEnabled('reportUnknownFlags')) {
138-
unknownToggleNames = toggleNames
139-
.filter((name) => !existingNames.includes(name))
140-
.slice(0, MAX_UNKNOWN_FLAGS);
146+
147+
if (shouldFilter) {
148+
toggleNamesToValidate = existingNames;
149+
150+
if (existingNames.length !== toggleNames.length) {
151+
this.logger.info(
152+
`Filtered out ${toggleNames.length - existingNames.length} toggles with non-existing names`,
153+
);
154+
}
141155
}
142-
if (existingNames.length !== toggleNames.length) {
143-
this.logger.info(
144-
`Filtered out ${toggleNames.length - existingNames.length} toggles with non-existing names`,
156+
157+
if (shouldReport) {
158+
unknownToggleNames = nonExistingNames.slice(
159+
0,
160+
MAX_UNKNOWN_FLAGS,
145161
);
146162
}
147-
148-
const validatedToggleNames =
149-
await this.filterValidToggleNames(existingNames);
150-
151-
return { validatedToggleNames, unknownToggleNames };
152163
} catch (e) {
153164
this.logger.error(e);
154165
}
155166
}
156167

157-
const validatedToggleNames =
158-
await this.filterValidToggleNames(toggleNames);
168+
const validatedToggleNames = await this.filterValidToggleNames(
169+
toggleNamesToValidate,
170+
);
159171

160172
return { validatedToggleNames, unknownToggleNames };
161173
}

src/lib/features/metrics/unknown-flags/fake-unknown-flags-store.ts

+4
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,8 @@ export class FakeUnknownFlagsStore implements IUnknownFlagsStore {
3030
async deleteAll(): Promise<void> {
3131
this.unknownFlagMap.clear();
3232
}
33+
34+
async count(): Promise<number> {
35+
return this.unknownFlagMap.size;
36+
}
3337
}

src/lib/features/metrics/unknown-flags/unknown-flags-controller.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { NONE } from '../../../types/permissions';
1313
import { serializeDates } from '../../../types/serialize-dates';
1414
import type { IUnleashServices } from '../../../types/services';
1515
import type { UnknownFlagsService } from './unknown-flags-service';
16+
import { NotFoundError } from '../../../error';
1617

1718
export default class UnknownFlagsController extends Controller {
1819
private unknownFlagsService: UnknownFlagsService;
@@ -54,9 +55,12 @@ export default class UnknownFlagsController extends Controller {
5455
}
5556

5657
async getUnknownFlags(
57-
req: IAuthRequest,
58+
_: IAuthRequest,
5859
res: Response<UnknownFlagsResponseSchema>,
5960
): Promise<void> {
61+
if (!this.flagResolver.isEnabled('reportUnknownFlags')) {
62+
throw new NotFoundError();
63+
}
6064
const unknownFlags =
6165
await this.unknownFlagsService.getGroupedUnknownFlags();
6266

src/lib/features/metrics/unknown-flags/unknown-flags-service.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import type { Logger } from '../../../logger';
2-
import type { IUnknownFlagsStore, IUnleashConfig } from '../../../types';
2+
import type {
3+
IFlagResolver,
4+
IUnknownFlagsStore,
5+
IUnleashConfig,
6+
} from '../../../types';
37
import type { IUnleashStores } from '../../../types';
48
import type { UnknownFlag } from './unknown-flags-store';
59

610
export const MAX_UNKNOWN_FLAGS = 10;
711

812
export class UnknownFlagsService {
9-
private config: IUnleashConfig;
10-
1113
private logger: Logger;
1214

15+
private flagResolver: IFlagResolver;
16+
1317
private unknownFlagsStore: IUnknownFlagsStore;
1418

1519
private unknownFlagsCache: Map<string, UnknownFlag>;
@@ -19,7 +23,7 @@ export class UnknownFlagsService {
1923
config: IUnleashConfig,
2024
) {
2125
this.unknownFlagsStore = unknownFlagsStore;
22-
this.config = config;
26+
this.flagResolver = config.flagResolver;
2327
this.logger = config.getLogger(
2428
'/features/metrics/unknown-flags/unknown-flags-service.ts',
2529
);
@@ -51,6 +55,7 @@ export class UnknownFlagsService {
5155
}
5256

5357
async flush(): Promise<void> {
58+
if (!this.flagResolver.isEnabled('reportUnknownFlags')) return;
5459
if (this.unknownFlagsCache.size === 0) return;
5560

5661
const existing = await this.unknownFlagsStore.getAll();
@@ -96,6 +101,7 @@ export class UnknownFlagsService {
96101
}
97102

98103
async clear(hoursAgo: number) {
104+
if (!this.flagResolver.isEnabled('reportUnknownFlags')) return;
99105
return this.unknownFlagsStore.clear(hoursAgo);
100106
}
101107
}

src/lib/features/metrics/unknown-flags/unknown-flags-store.ts

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface IUnknownFlagsStore {
1414
getAll(): Promise<UnknownFlag[]>;
1515
clear(hoursAgo: number): Promise<void>;
1616
deleteAll(): Promise<void>;
17+
count(): Promise<number>;
1718
}
1819

1920
export class UnknownFlagsStore implements IUnknownFlagsStore {
@@ -58,4 +59,9 @@ export class UnknownFlagsStore implements IUnknownFlagsStore {
5859
async deleteAll(): Promise<void> {
5960
await this.db(TABLE).delete();
6061
}
62+
63+
async count(): Promise<number> {
64+
const row = await this.db(TABLE).count('* as count').first();
65+
return Number(row?.count ?? 0);
66+
}
6167
}

src/lib/metrics.ts

+9
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,11 @@ export function registerPrometheusMetrics(
736736
labelNames: ['result', 'destination'],
737737
});
738738

739+
const unknownFlagsGauge = createGauge({
740+
name: 'unknown_flags',
741+
help: 'Number of unknown flags reported in the last 24 hours, if any. Maximum of 10.',
742+
});
743+
739744
// register event listeners
740745
eventBus.on(
741746
events.EXCEEDS_LIMIT,
@@ -1136,6 +1141,10 @@ export function registerPrometheusMetrics(
11361141
productionChanges60.set(productionChanges.last60);
11371142
productionChanges90.reset();
11381143
productionChanges90.set(productionChanges.last90);
1144+
1145+
const unknownFlags = await stores.unknownFlagsStore.count();
1146+
unknownFlagsGauge.reset();
1147+
unknownFlagsGauge.set(unknownFlags);
11391148
} catch (e) {}
11401149
},
11411150
};

0 commit comments

Comments
 (0)