Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: send traffic info to prometheus #8541

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/lib/features/instance-stats/createInstanceStatsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store';
import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store';
import { FeatureStrategiesReadModel } from '../feature-toggle/feature-strategies-read-model';
import { FakeFeatureStrategiesReadModel } from '../feature-toggle/fake-feature-strategies-read-model';
import { TrafficDataUsageStore } from '../traffic-data-usage/traffic-data-usage-store';
import { FakeTrafficDataUsageStore } from '../traffic-data-usage/fake-traffic-data-usage-store';

export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
const { eventBus, getLogger, flagResolver } = config;
Expand Down Expand Up @@ -93,6 +95,9 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
);

const featureStrategiesReadModel = new FeatureStrategiesReadModel(db);

const trafficDataUsageStore = new TrafficDataUsageStore(db, getLogger);

const instanceStatsServiceStores = {
featureToggleStore,
userStore,
Expand All @@ -109,6 +114,7 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
apiTokenStore,
clientMetricsStoreV2,
featureStrategiesReadModel,
trafficDataUsageStore,
};
const featureStrategiesStore = new FeatureStrategyStore(
db,
Expand Down Expand Up @@ -157,6 +163,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
const apiTokenStore = new FakeApiTokenStore();
const clientMetricsStoreV2 = new FakeClientMetricsStoreV2();
const featureStrategiesReadModel = new FakeFeatureStrategiesReadModel();
const trafficDataUsageStore = new FakeTrafficDataUsageStore();

const instanceStatsServiceStores = {
featureToggleStore,
Expand All @@ -174,6 +181,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
apiTokenStore,
clientMetricsStoreV2,
featureStrategiesReadModel,
trafficDataUsageStore,
};
const featureStrategiesStore = new FakeFeatureStrategiesStore();
const versionServiceStores = {
Expand Down
17 changes: 17 additions & 0 deletions src/lib/features/instance-stats/instance-stats-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
IClientMetricsStoreV2,
IEventStore,
IFeatureStrategiesReadModel,
ITrafficDataUsageStore,
IUnleashStores,
} from '../../types/stores';
import type { IContextFieldStore } from '../../types/stores/context-field-store';
Expand All @@ -29,6 +30,7 @@ import { CUSTOM_ROOT_ROLE_TYPE } from '../../util';
import type { GetActiveUsers } from './getActiveUsers';
import type { ProjectModeCount } from '../project/project-store';
import type { GetProductionChanges } from './getProductionChanges';
import { format } from 'date-fns';

export type TimeRange = 'allTime' | '30d' | '7d';

Expand Down Expand Up @@ -115,6 +117,8 @@ export class InstanceStatsService {

private featureStrategiesReadModel: IFeatureStrategiesReadModel;

private trafficDataUsageStore: ITrafficDataUsageStore;

constructor(
{
featureToggleStore,
Expand All @@ -132,6 +136,7 @@ export class InstanceStatsService {
apiTokenStore,
clientMetricsStoreV2,
featureStrategiesReadModel,
trafficDataUsageStore,
}: Pick<
IUnleashStores,
| 'featureToggleStore'
Expand All @@ -149,6 +154,7 @@ export class InstanceStatsService {
| 'apiTokenStore'
| 'clientMetricsStoreV2'
| 'featureStrategiesReadModel'
| 'trafficDataUsageStore'
>,
{
getLogger,
Expand Down Expand Up @@ -178,6 +184,7 @@ export class InstanceStatsService {
this.clientMetricsStore = clientMetricsStoreV2;
this.flagResolver = flagResolver;
this.featureStrategiesReadModel = featureStrategiesReadModel;
this.trafficDataUsageStore = trafficDataUsageStore;
}

getProjectModeCount(): Promise<ProjectModeCount[]> {
Expand Down Expand Up @@ -361,6 +368,16 @@ export class InstanceStatsService {
return this.userStore.countServiceAccounts();
}

async getCurrentTrafficData(): Promise<number> {
const traffic =
await this.trafficDataUsageStore.getTrafficDataUsageForPeriod(
format(new Date(), 'yyyy-MM'),
);

const counts = traffic.map((item) => item.count);
return counts.reduce((total, current) => total + current, 0);
}

async getLabeledAppCounts(): Promise<
Partial<{ [key in TimeRange]: number }>
> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import type {
IStatTrafficUsage,
} from './traffic-data-usage-store-type';
import type { ITrafficDataUsageStore } from '../../types';
import { isSameMonth, parse } from 'date-fns';

export class FakeTrafficDataUsageStore implements ITrafficDataUsageStore {
private trafficData: IStatTrafficUsage[] = [];

get(key: IStatTrafficUsageKey): Promise<IStatTrafficUsage> {
throw new Error('Method not implemented.');
}
Expand All @@ -23,10 +26,28 @@ export class FakeTrafficDataUsageStore implements ITrafficDataUsageStore {
destroy(): void {
throw new Error('Method not implemented.');
}
upsert(trafficDataUsage: IStatTrafficUsage): Promise<void> {
throw new Error('Method not implemented.');
async upsert(trafficDataUsage: IStatTrafficUsage): Promise<void> {
const index = this.trafficData.findIndex(
(data) =>
data.day.getTime() === trafficDataUsage.day.getTime() &&
data.trafficGroup === trafficDataUsage.trafficGroup &&
data.statusCodeSeries === trafficDataUsage.statusCodeSeries,
);

if (index >= 0) {
this.trafficData[index].count += trafficDataUsage.count;
} else {
this.trafficData.push(trafficDataUsage);
}
}
getTrafficDataUsageForPeriod(period: string): Promise<IStatTrafficUsage[]> {
throw new Error('Method not implemented.');

async getTrafficDataUsageForPeriod(
period: string,
): Promise<IStatTrafficUsage[]> {
const periodDate = parse(period, 'yyyy-MM', new Date());

return this.trafficData.filter((data) =>
isSameMonth(data.day, periodDate),
);
}
}
6 changes: 6 additions & 0 deletions src/lib/metrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,9 @@ test('should collect limit exceeded metrics', async () => {
/exceeds_limit_error{resource=\"feature flags\",limit=\"5000\"} 1/,
);
});

test('should collect traffic_total metrics', async () => {
const recordedMetric =
await prometheusRegister.getSingleMetricAsString('traffic_total');
expect(recordedMetric).toMatch(/traffic_total 0/);
});
9 changes: 9 additions & 0 deletions src/lib/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ export function registerPrometheusMetrics(
name: 'users_total',
help: 'Number of users',
});
const trafficTotal = createGauge({
name: 'traffic_total',
help: 'Traffic used current month',
});
const serviceAccounts = createGauge({
name: 'service_accounts_total',
help: 'Number of service accounts',
Expand Down Expand Up @@ -957,6 +961,11 @@ export function registerPrometheusMetrics(
await instanceStatsService.countServiceAccounts(),
);

trafficTotal.reset();
trafficTotal.set(
await instanceStatsService.getCurrentTrafficData(),
);

apiTokens.reset();

for (const [
Expand Down
Loading