mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: include environment type label in feature_toggle_update metrics (#5809)
This is needed in order to identify what type of an environment a toggle is updated in. This can be test, development, pre-production or production.
This commit is contained in:
parent
2590b4eaea
commit
24b202ef0b
@ -6,6 +6,7 @@ import { REQUEST_TIME, DB_TIME } from './metric-events';
|
||||
import {
|
||||
CLIENT_METRICS,
|
||||
CLIENT_REGISTER,
|
||||
FEATURE_ENVIRONMENT_ENABLED,
|
||||
FEATURE_UPDATED,
|
||||
} from './types/events';
|
||||
import { createMetricsMonitor } from './metrics';
|
||||
@ -14,11 +15,14 @@ import { InstanceStatsService } from './features/instance-stats/instance-stats-s
|
||||
import VersionService from './services/version-service';
|
||||
import { createFakeGetActiveUsers } from './features/instance-stats/getActiveUsers';
|
||||
import { createFakeGetProductionChanges } from './features/instance-stats/getProductionChanges';
|
||||
import { IEnvironmentStore } from './types';
|
||||
import FakeEnvironmentStore from './features/project-environments/fake-environment-store';
|
||||
|
||||
const monitor = createMetricsMonitor();
|
||||
const eventBus = new EventEmitter();
|
||||
const prometheusRegister = register;
|
||||
let eventStore: IEventStore;
|
||||
let environmentStore: IEnvironmentStore;
|
||||
let statsService: InstanceStatsService;
|
||||
let stores;
|
||||
beforeAll(() => {
|
||||
@ -29,6 +33,8 @@ beforeAll(() => {
|
||||
});
|
||||
stores = createStores();
|
||||
eventStore = stores.eventStore;
|
||||
environmentStore = new FakeEnvironmentStore();
|
||||
stores.environmentStore = environmentStore;
|
||||
const versionService = new VersionService(
|
||||
stores,
|
||||
config,
|
||||
@ -93,7 +99,32 @@ test('should collect metrics for updated toggles', async () => {
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(
|
||||
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="default"\} 1/,
|
||||
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="default",environmentType="production"\} 1/,
|
||||
);
|
||||
});
|
||||
|
||||
test('should set environmentType when toggle is flipped', async () => {
|
||||
await environmentStore.create({
|
||||
name: 'testEnvironment',
|
||||
enabled: true,
|
||||
type: 'testType',
|
||||
sortOrder: 1,
|
||||
});
|
||||
stores.eventStore.emit(FEATURE_ENVIRONMENT_ENABLED, {
|
||||
featureName: 'TestToggle',
|
||||
project: 'default',
|
||||
environment: 'testEnvironment',
|
||||
data: { name: 'TestToggle' },
|
||||
});
|
||||
|
||||
// Wait for event to be processed, not nice, but it works.
|
||||
await new Promise((done) => {
|
||||
setTimeout(done, 1);
|
||||
});
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
|
||||
expect(metrics).toMatch(
|
||||
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="testEnvironment",environmentType="testType"\} 1/,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import client from 'prom-client';
|
||||
import memoizee from 'memoizee';
|
||||
import EventEmitter from 'events';
|
||||
import { Knex } from 'knex';
|
||||
import * as events from './metric-events';
|
||||
@ -19,11 +20,12 @@ import {
|
||||
CLIENT_REGISTER,
|
||||
} from './types/events';
|
||||
import { IUnleashConfig } from './types/option';
|
||||
import { IUnleashStores } from './types/stores';
|
||||
import { IEnvironmentStore, IUnleashStores } from './types/stores';
|
||||
import { hoursToMilliseconds, minutesToMilliseconds } from 'date-fns';
|
||||
import Timer = NodeJS.Timer;
|
||||
import { InstanceStatsService } from './features/instance-stats/instance-stats-service';
|
||||
import { ValidatedClientMetrics } from './services/client-metrics/schema';
|
||||
import { IEnvironment } from './types';
|
||||
|
||||
export default class MetricsMonitor {
|
||||
timer?: Timer;
|
||||
@ -47,7 +49,15 @@ export default class MetricsMonitor {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const { eventStore } = stores;
|
||||
const { eventStore, environmentStore } = stores;
|
||||
|
||||
const cachedEnvironments: () => Promise<IEnvironment[]> = memoizee(
|
||||
async () => environmentStore.getAll(),
|
||||
{
|
||||
promise: true,
|
||||
maxAge: hoursToMilliseconds(1),
|
||||
},
|
||||
);
|
||||
|
||||
client.collectDefaultMetrics();
|
||||
|
||||
@ -78,7 +88,7 @@ export default class MetricsMonitor {
|
||||
const featureToggleUpdateTotal = new client.Counter({
|
||||
name: 'feature_toggle_update_total',
|
||||
help: 'Number of times a toggle has been updated. Environment label would be "n/a" when it is not available, e.g. when a feature toggle is created.',
|
||||
labelNames: ['toggle', 'project', 'environment'],
|
||||
labelNames: ['toggle', 'project', 'environment', 'environmentType'],
|
||||
});
|
||||
const featureToggleUsageTotal = new client.Counter({
|
||||
name: 'feature_toggle_usage_total',
|
||||
@ -360,56 +370,82 @@ export default class MetricsMonitor {
|
||||
});
|
||||
|
||||
eventStore.on(FEATURE_CREATED, ({ featureName, project }) => {
|
||||
featureToggleUpdateTotal.labels(featureName, project, 'n/a').inc();
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, 'n/a', 'n/a')
|
||||
.inc();
|
||||
});
|
||||
eventStore.on(FEATURE_VARIANTS_UPDATED, ({ featureName, project }) => {
|
||||
featureToggleUpdateTotal.labels(featureName, project, 'n/a').inc();
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, 'n/a', 'n/a')
|
||||
.inc();
|
||||
});
|
||||
eventStore.on(FEATURE_METADATA_UPDATED, ({ featureName, project }) => {
|
||||
featureToggleUpdateTotal.labels(featureName, project, 'n/a').inc();
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, 'n/a', 'n/a')
|
||||
.inc();
|
||||
});
|
||||
eventStore.on(FEATURE_UPDATED, ({ featureName, project }) => {
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, 'default')
|
||||
.labels(featureName, project, 'default', 'production')
|
||||
.inc();
|
||||
});
|
||||
eventStore.on(
|
||||
FEATURE_STRATEGY_ADD,
|
||||
({ featureName, project, environment }) => {
|
||||
async ({ featureName, project, environment }) => {
|
||||
const environmentType = await this.resolveEnvironmentType(
|
||||
environment,
|
||||
cachedEnvironments,
|
||||
);
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, environment)
|
||||
.labels(featureName, project, environment, environmentType)
|
||||
.inc();
|
||||
},
|
||||
);
|
||||
eventStore.on(
|
||||
FEATURE_STRATEGY_REMOVE,
|
||||
({ featureName, project, environment }) => {
|
||||
async ({ featureName, project, environment }) => {
|
||||
const environmentType = await this.resolveEnvironmentType(
|
||||
environment,
|
||||
cachedEnvironments,
|
||||
);
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, environment)
|
||||
.labels(featureName, project, environment, environmentType)
|
||||
.inc();
|
||||
},
|
||||
);
|
||||
eventStore.on(
|
||||
FEATURE_STRATEGY_UPDATE,
|
||||
({ featureName, project, environment }) => {
|
||||
async ({ featureName, project, environment }) => {
|
||||
const environmentType = await this.resolveEnvironmentType(
|
||||
environment,
|
||||
cachedEnvironments,
|
||||
);
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, environment)
|
||||
.labels(featureName, project, environment, environmentType)
|
||||
.inc();
|
||||
},
|
||||
);
|
||||
eventStore.on(
|
||||
FEATURE_ENVIRONMENT_DISABLED,
|
||||
({ featureName, project, environment }) => {
|
||||
async ({ featureName, project, environment }) => {
|
||||
const environmentType = await this.resolveEnvironmentType(
|
||||
environment,
|
||||
cachedEnvironments,
|
||||
);
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, environment)
|
||||
.labels(featureName, project, environment, environmentType)
|
||||
.inc();
|
||||
},
|
||||
);
|
||||
eventStore.on(
|
||||
FEATURE_ENVIRONMENT_ENABLED,
|
||||
({ featureName, project, environment }) => {
|
||||
async ({ featureName, project, environment }) => {
|
||||
const environmentType = await this.resolveEnvironmentType(
|
||||
environment,
|
||||
cachedEnvironments,
|
||||
);
|
||||
featureToggleUpdateTotal
|
||||
.labels(featureName, project, environment)
|
||||
.labels(featureName, project, environment, environmentType)
|
||||
.inc();
|
||||
},
|
||||
);
|
||||
@ -504,6 +540,20 @@ export default class MetricsMonitor {
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async resolveEnvironmentType(
|
||||
environment: string,
|
||||
cachedEnvironments: () => Promise<IEnvironment[]>,
|
||||
): Promise<string> {
|
||||
const environments = await cachedEnvironments();
|
||||
const env = environments.find((e) => e.name === environment);
|
||||
|
||||
if (env) {
|
||||
return env.type;
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
export function createMetricsMonitor(): MetricsMonitor {
|
||||
return new MetricsMonitor();
|
||||
|
Loading…
Reference in New Issue
Block a user