Skip to content

Commit

Permalink
fix: add all instance stats to metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
ivarconr committed Oct 21, 2022
1 parent f933d35 commit 14625b9
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 41 deletions.
17 changes: 16 additions & 1 deletion src/lib/metrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import {
} from './types/events';
import { createMetricsMonitor } from './metrics';
import createStores from '../test/fixtures/store';
import { InstanceStatsService } from './services/instance-stats-service';
import VersionService from './services/version-service';

const monitor = createMetricsMonitor();
const eventBus = new EventEmitter();
const prometheusRegister = register;
let eventStore: IEventStore;
let statsService: InstanceStatsService;
let stores;
beforeAll(() => {
const config = createTestConfig({
Expand All @@ -24,6 +27,8 @@ beforeAll(() => {
});
stores = createStores();
eventStore = stores.eventStore;
const versionService = new VersionService(stores, config);
statsService = new InstanceStatsService(stores, config, versionService);
const db = {
client: {
pool: {
Expand All @@ -37,7 +42,14 @@ beforeAll(() => {
},
};
// @ts-ignore - We don't want a full knex implementation for our tests, it's enough that it actually yields the numbers we want.
monitor.startMonitoring(config, stores, '4.0.0', eventBus, db);
monitor.startMonitoring(
config,
stores,
'4.0.0',
eventBus,
statsService,
db,
);
});
afterAll(() => {
monitor.stopMonitoring();
Expand Down Expand Up @@ -102,6 +114,9 @@ test('should collect metrics for db query timings', async () => {
});

test('should collect metrics for feature toggle size', async () => {
await new Promise((done) => {
setTimeout(done, 10);
});
const metrics = await prometheusRegister.metrics();
expect(metrics).toMatch(/feature_toggles_total{version="(.*)"} 0/);
});
Expand Down
110 changes: 75 additions & 35 deletions src/lib/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { IUnleashConfig } from './types/option';
import { IUnleashStores } from './types/stores';
import { hoursToMilliseconds, minutesToMilliseconds } from 'date-fns';
import Timer = NodeJS.Timer;
import { InstanceStatsService } from './services/instance-stats-service';

export default class MetricsMonitor {
timer?: Timer;
Expand All @@ -38,19 +39,14 @@ export default class MetricsMonitor {
stores: IUnleashStores,
version: string,
eventBus: EventEmitter,
instanceStatsService: InstanceStatsService,
db: Knex,
): Promise<void> {
if (!config.server.serverMetrics) {
return;
}

const {
eventStore,
featureToggleStore,
userStore,
projectStore,
environmentStore,
} = stores;
const { eventStore } = stores;

client.collectDefaultMetrics();

Expand Down Expand Up @@ -97,6 +93,40 @@ export default class MetricsMonitor {
name: 'environments_total',
help: 'Number of environments',
});
const groupsTotal = new client.Gauge({
name: 'groups_total',
help: 'Number of groups',
});

const rolesTotal = new client.Gauge({
name: 'roles_total',
help: 'Number of roles',
});

const segmentsTotal = new client.Gauge({
name: 'segments_total',
help: 'Number of segments',
});

const contextTotal = new client.Gauge({
name: 'context_total',
help: 'Number of context',
});

const strategiesTotal = new client.Gauge({
name: 'strategies_total',
help: 'Number of strategies',
});

const samlEnabled = new client.Gauge({
name: 'saml_enabled',
help: 'Whether SAML is enabled',
});

const oidcEnabled = new client.Gauge({
name: 'oidc_enabled',
help: 'Whether OIDC is enabled',
});

const clientSdkVersionUsage = new client.Counter({
name: 'client_sdk_versions',
Expand All @@ -105,41 +135,51 @@ export default class MetricsMonitor {
});

async function collectStaticCounters() {
let togglesCount: number = 0;
let usersCount: number;
let projectsCount: number;
let environmentsCount: number;
try {
togglesCount = await featureToggleStore.count({
archived: false,
});
usersCount = await userStore.count();
projectsCount = await projectStore.count();
environmentsCount = await environmentStore.count();
// eslint-disable-next-line no-empty
} catch (e) {}
const stats = await instanceStatsService.getStats();

featureTogglesTotal.reset();
featureTogglesTotal.labels(version).set(stats.featureToggles);

featureTogglesTotal.reset();
featureTogglesTotal.labels(version).set(togglesCount);
if (usersCount) {
usersTotal.reset();
usersTotal.set(usersCount);
}
if (projectsCount) {
usersTotal.set(stats.users);

projectsTotal.reset();
projectsTotal.set(projectsCount);
}
if (environmentsCount) {
projectsTotal.set(stats.projects);

environmentsTotal.reset();
environmentsTotal.set(environmentsCount);
}
environmentsTotal.set(stats.environments);

groupsTotal.reset();
groupsTotal.set(stats.groups);

rolesTotal.reset();
rolesTotal.set(stats.roles);

segmentsTotal.reset();
segmentsTotal.set(stats.segments);

contextTotal.reset();
contextTotal.set(stats.contextFields);

strategiesTotal.reset();
strategiesTotal.set(stats.strategies);

samlEnabled.reset();
samlEnabled.set(stats.SAMLenabled ? 1 : 0);

oidcEnabled.reset();
oidcEnabled.set(stats.OIDCenabled ? 1 : 0);
} catch (e) {}
}

collectStaticCounters();
this.timer = setInterval(
() => collectStaticCounters(),
hoursToMilliseconds(2),
).unref();
process.nextTick(() => {
collectStaticCounters();
this.timer = setInterval(
() => collectStaticCounters(),
hoursToMilliseconds(2),
).unref();
});

eventBus.on(
events.REQUEST_TIME,
Expand Down
1 change: 1 addition & 0 deletions src/lib/server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ async function createApp(
stores,
serverVersion,
config.eventBus,
services.instanceStatsService,
db,
);
const unleash: Omit<IUnleash, 'stop'> = {
Expand Down
2 changes: 1 addition & 1 deletion src/test/fixtures/fake-context-field-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import NotFoundError from '../../lib/error/notfound-error';

export default class FakeContextFieldStore implements IContextFieldStore {
count(): Promise<number> {
throw new Error('Method not implemented.');
return Promise.resolve(0);
}

defaultContextFields: IContextField[] = [
Expand Down
2 changes: 1 addition & 1 deletion src/test/fixtures/fake-group-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Group, {
/* eslint-disable @typescript-eslint/no-unused-vars */
export default class FakeGroupStore implements IGroupStore {
count(): Promise<number> {
throw new Error('Method not implemented.');
return Promise.resolve(0);
}

data: IGroup[];
Expand Down
2 changes: 1 addition & 1 deletion src/test/fixtures/fake-role-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {

export default class FakeRoleStore implements IRoleStore {
count(): Promise<number> {
throw new Error('Method not implemented.');
return Promise.resolve(0);
}

getProjectRolesCount(): Promise<number> {
Expand Down
2 changes: 1 addition & 1 deletion src/test/fixtures/fake-segment-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IFeatureStrategySegment, ISegment } from '../../lib/types/model';

export default class FakeSegmentStore implements ISegmentStore {
count(): Promise<number> {
throw new Error('Method not implemented.');
return Promise.resolve(0);
}

create(): Promise<ISegment> {
Expand Down
2 changes: 1 addition & 1 deletion src/test/fixtures/fake-strategies-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import NotFoundError from '../../lib/error/notfound-error';

export default class FakeStrategiesStore implements IStrategyStore {
count(): Promise<number> {
throw new Error('Method not implemented.');
return Promise.resolve(0);
}

defaultStrategy: IStrategy = {
Expand Down

0 comments on commit 14625b9

Please sign in to comment.