1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-10 01:19:53 +01:00

Migrate more metrics

This commit is contained in:
Gastón Fournier 2024-10-18 16:56:02 +02:00
parent 9015cce47b
commit d6577d4b80
No known key found for this signature in database
GPG Key ID: AF45428626E17A8E
2 changed files with 117 additions and 111 deletions

View File

@ -37,17 +37,39 @@ export class DbMetricsMonitor {
return Array.isArray(value) ? value : [value]; return Array.isArray(value) ? value : [value];
} }
private async fetch<T, L extends string>(
definition: GaugeDefinition<T, L>,
) {
const result = await definition.query();
if (
result !== undefined &&
result !== null &&
(!Array.isArray(result) || result.length > 0)
) {
const resultArray = this.asArray(definition.map(result));
resultArray
.filter((r) => !Number.isInteger(r.value))
.forEach((r) => {
this.log.warn(
`Invalid value for ${definition.name}: ${r.value}. Value must be an integer.`,
);
});
return resultArray.filter((r) => Number.isInteger(r.value));
}
return [];
}
registerGaugeDbMetric<T, L extends string>( registerGaugeDbMetric<T, L extends string>(
definition: GaugeDefinition<T, L>, definition: GaugeDefinition<T, L>,
): Task { ): Task {
const gauge = createGauge(definition); const gauge = createGauge(definition);
const task = async () => { const task = async () => {
try { try {
const result = await definition.query(); const results = await this.fetch(definition);
if (result !== null && result !== undefined) { if (results.length > 0) {
const results = this.asArray(definition.map(result));
gauge.reset(); gauge.reset();
for (const r of results) { for (const r of results) {
// when r.value is zero, we are writing a zero value to the gauge which might not be what we want in some cases
if (r.labels) { if (r.labels) {
gauge.labels(r.labels).set(r.value); gauge.labels(r.labels).set(r.value);
} else { } else {

View File

@ -219,20 +219,54 @@ export function registerPrometheusMetrics(
}), }),
}); });
const maxConstraintsPerStrategy = createGauge({ dbMetrics.registerGaugeDbMetric({
name: 'max_strategy_constraints', name: 'max_strategy_constraints',
help: 'Maximum number of constraints used on a single strategy', help: 'Maximum number of constraints used on a single strategy',
labelNames: ['feature', 'environment'], labelNames: ['feature', 'environment'],
query: () =>
stores.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
map: (result) => ({
value: result.count,
labels: {
environment: result.environment,
feature: result.feature,
},
}),
}); });
const largestProjectEnvironment = createGauge({
dbMetrics.registerGaugeDbMetric({
name: 'largest_project_environment_size', name: 'largest_project_environment_size',
help: 'The largest project environment size (bytes) based on strategies, constraints, variants and parameters', help: 'The largest project environment size (bytes) based on strategies, constraints, variants and parameters',
labelNames: ['project', 'environment'], labelNames: ['project', 'environment'],
query: () =>
stores.largestResourcesReadModel.getLargestProjectEnvironments(1),
map: (results) => {
const result = results[0];
return {
value: result.size,
labels: {
project: result.project,
environment: result.environment,
},
};
},
}); });
const largestFeatureEnvironment = createGauge({ dbMetrics.registerGaugeDbMetric({
name: 'largest_feature_environment_size', name: 'largest_feature_environment_size',
help: 'The largest feature environment size (bytes) base on strategies, constraints, variants and parameters', help: 'The largest feature environment size (bytes) base on strategies, constraints, variants and parameters',
labelNames: ['feature', 'environment'], labelNames: ['feature', 'environment'],
query: () =>
stores.largestResourcesReadModel.getLargestFeatureEnvironments(1),
map: (results) => {
const result = results[0];
return {
value: result.size,
labels: {
feature: result.feature,
environment: result.environment,
},
};
},
}); });
const featureTogglesArchivedTotal = createGauge({ const featureTogglesArchivedTotal = createGauge({
@ -473,27 +507,76 @@ export function registerPrometheusMetrics(
help: 'Duration of mapFeaturesForClient function', help: 'Duration of mapFeaturesForClient function',
}); });
const featureLifecycleStageDuration = createGauge({ dbMetrics.registerGaugeDbMetric({
name: 'feature_lifecycle_stage_duration', name: 'feature_lifecycle_stage_duration',
labelNames: ['stage', 'project_id'], labelNames: ['stage', 'project_id'],
help: 'Duration of feature lifecycle stages', help: 'Duration of feature lifecycle stages',
query: () => stores.featureLifecycleReadModel.getAllWithStageDuration(),
map: (result) =>
result.map((stageResult) => ({
value: stageResult.duration,
labels: {
project_id: stageResult.project,
stage: stageResult.stage,
},
})),
}); });
const onboardingDuration = createGauge({ dbMetrics.registerGaugeDbMetric({
name: 'onboarding_duration', name: 'onboarding_duration',
labelNames: ['event'], labelNames: ['event'],
help: 'firstLogin, secondLogin, firstFeatureFlag, firstPreLive, firstLive from first user creation', help: 'firstLogin, secondLogin, firstFeatureFlag, firstPreLive, firstLive from first user creation',
query: () =>
flagResolver.isEnabled('onboardingMetrics')
? stores.onboardingReadModel.getInstanceOnboardingMetrics()
: Promise.resolve({}),
map: (result) =>
Object.keys(result)
.filter((key) => Number.isInteger(result[key]))
.map((key) => ({
value: result[key],
labels: {
event: key,
},
})),
}); });
const projectOnboardingDuration = createGauge({
dbMetrics.registerGaugeDbMetric({
name: 'project_onboarding_duration', name: 'project_onboarding_duration',
labelNames: ['event', 'project'], labelNames: ['event', 'project'],
help: 'firstFeatureFlag, firstPreLive, firstLive from project creation', help: 'firstFeatureFlag, firstPreLive, firstLive from project creation',
query: () =>
flagResolver.isEnabled('onboardingMetrics')
? stores.onboardingReadModel.getProjectsOnboardingMetrics()
: Promise.resolve([]),
map: (projectsOnboardingMetrics) =>
projectsOnboardingMetrics.flatMap(
({ project, ...projectMetrics }) =>
Object.keys(projectMetrics)
.filter((key) => Number.isInteger(projectMetrics[key]))
.map((key) => ({
value: projectMetrics[key],
labels: {
event: key,
project,
},
})),
),
}); });
const featureLifecycleStageCountByProject = createGauge({ dbMetrics.registerGaugeDbMetric({
name: 'feature_lifecycle_stage_count_by_project', name: 'feature_lifecycle_stage_count_by_project',
help: 'Count features in a given stage by project id', help: 'Count features in a given stage by project id',
labelNames: ['stage', 'project_id'], labelNames: ['stage', 'project_id'],
query: () => stores.featureLifecycleReadModel.getStageCountByProject(),
map: (result) =>
result.map((stageResult) => ({
value: stageResult.count,
labels: {
project_id: stageResult.project,
stage: stageResult.stage,
},
})),
}); });
const featureLifecycleStageEnteredCounter = createCounter({ const featureLifecycleStageEnteredCounter = createCounter({
@ -858,34 +941,6 @@ export function registerPrometheusMetrics(
collectDbMetrics: dbMetrics.refreshDbMetrics, collectDbMetrics: dbMetrics.refreshDbMetrics,
collectStaticCounters: async () => { collectStaticCounters: async () => {
try { try {
const [
maxConstraintsPerStrategyResult,
stageCountByProjectResult,
stageDurationByProject,
largestProjectEnvironments,
largestFeatureEnvironments,
deprecatedTokens,
instanceOnboardingMetrics,
projectsOnboardingMetrics,
] = await Promise.all([
stores.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
stores.featureLifecycleReadModel.getStageCountByProject(),
stores.featureLifecycleReadModel.getAllWithStageDuration(),
stores.largestResourcesReadModel.getLargestProjectEnvironments(
1,
),
stores.largestResourcesReadModel.getLargestFeatureEnvironments(
1,
),
stores.apiTokenStore.countDeprecatedTokens(),
flagResolver.isEnabled('onboardingMetrics')
? stores.onboardingReadModel.getInstanceOnboardingMetrics()
: Promise.resolve({}),
flagResolver.isEnabled('onboardingMetrics')
? stores.onboardingReadModel.getProjectsOnboardingMetrics()
: Promise.resolve([]),
]);
featureTogglesArchivedTotal.reset(); featureTogglesArchivedTotal.reset();
featureTogglesArchivedTotal.set( featureTogglesArchivedTotal.set(
await instanceStatsService.getArchivedToggleCount(), await instanceStatsService.getArchivedToggleCount(),
@ -899,25 +954,6 @@ export function registerPrometheusMetrics(
await instanceStatsService.countServiceAccounts(), await instanceStatsService.countServiceAccounts(),
); );
stageDurationByProject.forEach((stage) => {
featureLifecycleStageDuration
.labels({
stage: stage.stage,
project_id: stage.project,
})
.set(stage.duration);
});
featureLifecycleStageCountByProject.reset();
stageCountByProjectResult.forEach((stageResult) =>
featureLifecycleStageCountByProject
.labels({
project_id: stageResult.project,
stage: stageResult.stage,
})
.set(stageResult.count),
);
apiTokens.reset(); apiTokens.reset();
for (const [ for (const [
@ -927,6 +963,8 @@ export function registerPrometheusMetrics(
apiTokens.labels({ type }).set(value); apiTokens.labels({ type }).set(value);
} }
const deprecatedTokens =
await stores.apiTokenStore.countDeprecatedTokens();
orphanedTokensTotal.reset(); orphanedTokensTotal.reset();
orphanedTokensTotal.set(deprecatedTokens.orphanedTokens); orphanedTokensTotal.set(deprecatedTokens.orphanedTokens);
@ -939,60 +977,6 @@ export function registerPrometheusMetrics(
legacyTokensActive.reset(); legacyTokensActive.reset();
legacyTokensActive.set(deprecatedTokens.activeLegacyTokens); legacyTokensActive.set(deprecatedTokens.activeLegacyTokens);
if (maxConstraintsPerStrategyResult) {
maxConstraintsPerStrategy.reset();
maxConstraintsPerStrategy
.labels({
environment:
maxConstraintsPerStrategyResult.environment,
feature: maxConstraintsPerStrategyResult.feature,
})
.set(maxConstraintsPerStrategyResult.count);
}
if (largestProjectEnvironments.length > 0) {
const projectEnvironment = largestProjectEnvironments[0];
largestProjectEnvironment.reset();
largestProjectEnvironment
.labels({
project: projectEnvironment.project,
environment: projectEnvironment.environment,
})
.set(projectEnvironment.size);
}
if (largestFeatureEnvironments.length > 0) {
const featureEnvironment = largestFeatureEnvironments[0];
largestFeatureEnvironment.reset();
largestFeatureEnvironment
.labels({
feature: featureEnvironment.feature,
environment: featureEnvironment.environment,
})
.set(featureEnvironment.size);
}
Object.keys(instanceOnboardingMetrics).forEach((key) => {
if (Number.isInteger(instanceOnboardingMetrics[key])) {
onboardingDuration
.labels({
event: key,
})
.set(instanceOnboardingMetrics[key]);
}
});
projectsOnboardingMetrics.forEach(
({ project, ...projectMetrics }) => {
Object.keys(projectMetrics).forEach((key) => {
if (Number.isInteger(projectMetrics[key])) {
projectOnboardingDuration
.labels({ event: key, project })
.set(projectMetrics[key]);
}
});
},
);
for (const [resource, limit] of Object.entries( for (const [resource, limit] of Object.entries(
config.resourceLimits, config.resourceLimits,
)) { )) {