diff --git a/src/lib/db/project-store.ts b/src/lib/db/project-store.ts index b596a35cca4..543877317cb 100644 --- a/src/lib/db/project-store.ts +++ b/src/lib/db/project-store.ts @@ -231,6 +231,13 @@ class ProjectStore implements IProjectStore { return []; } + async count(): Promise { + return this.db + .count('*') + .from(TABLE) + .then((res) => Number(res[0].count)); + } + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types private getEnvironment(r: any): IEnvironmentOverview { return { diff --git a/src/lib/db/user-store.ts b/src/lib/db/user-store.ts index dff6ef35ae9..ea36ad0ebf4 100644 --- a/src/lib/db/user-store.ts +++ b/src/lib/db/user-store.ts @@ -176,6 +176,13 @@ class UserStore implements IUserStore { await this.db(TABLE).del(); } + async count(): Promise { + return this.db + .count('*') + .from(TABLE) + .then((res) => Number(res[0].count)); + } + destroy(): void {} async exists(id: number): Promise { diff --git a/src/lib/metrics.ts b/src/lib/metrics.ts index 38b3fc2c4f4..644f6fd1169 100644 --- a/src/lib/metrics.ts +++ b/src/lib/metrics.ts @@ -13,7 +13,7 @@ import { IUnleashConfig } from './types/option'; import { IUnleashStores } from './types/stores'; import Timer = NodeJS.Timer; -const THREE_HOURS = 3 * 60 * 60 * 1000; +const TWO_HOURS = 2 * 60 * 60 * 1000; const ONE_MINUTE = 60 * 1000; export default class MetricsMonitor { @@ -37,7 +37,13 @@ export default class MetricsMonitor { return; } - const { eventStore, clientMetricsStore, featureToggleStore } = stores; + const { + eventStore, + clientMetricsStore, + featureToggleStore, + userStore, + projectStore, + } = stores; client.collectDefaultMetrics(); @@ -68,25 +74,44 @@ export default class MetricsMonitor { help: 'Number of feature toggles', labelNames: ['version'], }); + const usersTotal = new client.Gauge({ + name: 'users_total', + help: 'Number of users', + }); + const projectsTotal = new client.Gauge({ + name: 'projects_total', + help: 'Number of projects', + }); - async function collectFeatureToggleMetrics() { - featureTogglesTotal.reset(); - let togglesCount; + async function collectStaticCounters() { + let togglesCount: number = 0; + let usersCount: number; + let projectsCount: number; try { togglesCount = await featureToggleStore.count({ archived: false, }); + usersCount = await userStore.count(); + projectsCount = await projectStore.count(); // eslint-disable-next-line no-empty } catch (e) {} - togglesCount = togglesCount || 0; + featureTogglesTotal.reset(); featureTogglesTotal.labels(version).set(togglesCount); + if (usersCount) { + usersTotal.reset(); + usersTotal.set(usersCount); + } + if (projectsCount) { + projectsTotal.reset(); + projectsTotal.set(usersCount); + } } - collectFeatureToggleMetrics(); + collectStaticCounters(); this.timer = setInterval( - () => collectFeatureToggleMetrics(), - THREE_HOURS, + () => collectStaticCounters(), + TWO_HOURS, ).unref(); eventBus.on( diff --git a/src/lib/types/stores/project-store.ts b/src/lib/types/stores/project-store.ts index 49102b1f997..e217b51b863 100644 --- a/src/lib/types/stores/project-store.ts +++ b/src/lib/types/stores/project-store.ts @@ -31,4 +31,5 @@ export interface IProjectStore extends Store { projectId: string, archived: boolean, ): Promise; + count(): Promise; } diff --git a/src/lib/types/stores/user-store.ts b/src/lib/types/stores/user-store.ts index f445eb4d7f7..67a00c306d7 100644 --- a/src/lib/types/stores/user-store.ts +++ b/src/lib/types/stores/user-store.ts @@ -37,4 +37,5 @@ export interface IUserStore extends Store { setPasswordHash(userId: number, passwordHash: string): Promise; incLoginAttempts(user: IUser): Promise; successfullyLogin(user: IUser): Promise; + count(): Promise; } diff --git a/src/test/fixtures/fake-project-store.ts b/src/test/fixtures/fake-project-store.ts index 7bfc9b9b8ce..93dea6f6700 100644 --- a/src/test/fixtures/fake-project-store.ts +++ b/src/test/fixtures/fake-project-store.ts @@ -64,6 +64,10 @@ export default class FakeProjectStore implements IProjectStore { destroy(): void {} + async count(): Promise { + return this.projects.length; + } + async exists(key: string): Promise { return this.projects.some((p) => p.id === key); } diff --git a/src/test/fixtures/fake-user-store.ts b/src/test/fixtures/fake-user-store.ts index 66bc97d2f46..3a0a96dc861 100644 --- a/src/test/fixtures/fake-user-store.ts +++ b/src/test/fixtures/fake-user-store.ts @@ -38,6 +38,10 @@ class UserStoreMock implements IUserStore { return this.data.some((u) => u.id === key); } + async count(): Promise { + return this.data.length; + } + async get(key: number): Promise { return this.data.find((u) => u.id === key); }