mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: sanitize impact metrics (#10364)
This commit is contained in:
parent
84748aaff0
commit
f04dd454d9
@ -2,6 +2,38 @@ import { MetricsTranslator } from './metrics-translator.js';
|
||||
import { Registry } from 'prom-client';
|
||||
|
||||
describe('MetricsTranslator', () => {
|
||||
describe('Sanitize name', () => {
|
||||
let translator: MetricsTranslator;
|
||||
|
||||
beforeEach(() => {
|
||||
const registry = new Registry();
|
||||
translator = new MetricsTranslator(registry);
|
||||
});
|
||||
|
||||
it('should not modify valid names', () => {
|
||||
expect(translator.sanitizeName('valid_name')).toBe('valid_name');
|
||||
expect(translator.sanitizeName('validName')).toBe('validName');
|
||||
expect(translator.sanitizeName('_valid_name')).toBe('_valid_name');
|
||||
});
|
||||
|
||||
it('should replace invalid characters with underscores', () => {
|
||||
expect(translator.sanitizeName('invalid-name')).toBe(
|
||||
'invalid_name',
|
||||
);
|
||||
expect(translator.sanitizeName('invalid.name')).toBe(
|
||||
'invalid_name',
|
||||
);
|
||||
expect(translator.sanitizeName('invalid@name')).toBe(
|
||||
'invalid_name',
|
||||
);
|
||||
expect(translator.sanitizeName('invalid name')).toBe(
|
||||
'invalid_name',
|
||||
);
|
||||
expect(translator.sanitizeName('invalid:name')).toBe(
|
||||
'invalid_name',
|
||||
);
|
||||
});
|
||||
});
|
||||
it('should handle metrics with labels', async () => {
|
||||
const metrics = [
|
||||
{
|
||||
@ -81,6 +113,59 @@ describe('MetricsTranslator', () => {
|
||||
expect(result).not.toContain('unsupported');
|
||||
});
|
||||
|
||||
it('should sanitize metric names and label names', async () => {
|
||||
const registry = new Registry();
|
||||
const translator = new MetricsTranslator(registry);
|
||||
|
||||
const metrics = [
|
||||
{
|
||||
name: 'invalid-metric-name',
|
||||
help: 'metric with invalid name',
|
||||
type: 'counter' as const,
|
||||
samples: [
|
||||
{
|
||||
labels: { 'invalid-label': 'value', '1numeric': 123 },
|
||||
value: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '1numeric-metric',
|
||||
help: 'metric with numeric prefix',
|
||||
type: 'gauge' as const,
|
||||
samples: [
|
||||
{
|
||||
labels: {
|
||||
'invalid:colon': 'value',
|
||||
'space label': 'test',
|
||||
},
|
||||
value: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = await translator.translateAndSerializeMetrics(metrics);
|
||||
|
||||
expect(result).toContain(
|
||||
'# HELP unleash_counter_invalid_metric_name metric with invalid name',
|
||||
);
|
||||
expect(result).toContain(
|
||||
'# TYPE unleash_counter_invalid_metric_name counter',
|
||||
);
|
||||
expect(result).toContain(
|
||||
'# HELP unleash_gauge_1numeric_metric metric with numeric prefix',
|
||||
);
|
||||
expect(result).toContain('# TYPE unleash_gauge_1numeric_metric gauge');
|
||||
|
||||
expect(result).toContain(
|
||||
'unleash_counter_invalid_metric_name{unleash_invalid_label="value",unleash_1numeric="123",unleash_origin="sdk"} 5',
|
||||
);
|
||||
expect(result).toContain(
|
||||
'unleash_gauge_1numeric_metric{unleash_invalid_colon="value",unleash_space_label="test",unleash_origin="sdk"} 10',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle re-labeling of metrics', async () => {
|
||||
const registry = new Registry();
|
||||
const translator = new MetricsTranslator(registry);
|
||||
|
@ -19,6 +19,14 @@ export class MetricsTranslator {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
sanitizeName(name: string): string {
|
||||
const regex = /[^a-zA-Z0-9_]/g;
|
||||
|
||||
const sanitized = name.replace(regex, '_');
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
private hasNewLabels(
|
||||
existingMetric: Counter<string> | Gauge<string>,
|
||||
newLabelNames: string[],
|
||||
@ -35,7 +43,7 @@ export class MetricsTranslator {
|
||||
): Record<string, string | number> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(labels).map(([labelKey, value]) => [
|
||||
`unleash_${labelKey}`,
|
||||
`unleash_${this.sanitizeName(labelKey)}`,
|
||||
value,
|
||||
]),
|
||||
);
|
||||
@ -51,7 +59,8 @@ export class MetricsTranslator {
|
||||
}
|
||||
|
||||
translateMetric(metric: Metric): Counter<string> | Gauge<string> | null {
|
||||
const prefixedName = `unleash_${metric.type}_${metric.name}`;
|
||||
const sanitizedName = this.sanitizeName(metric.name);
|
||||
const prefixedName = `unleash_${metric.type}_${sanitizedName}`;
|
||||
const existingMetric = this.registry.getSingleMetric(prefixedName);
|
||||
|
||||
const allLabelNames = new Set<string>();
|
||||
@ -63,7 +72,9 @@ export class MetricsTranslator {
|
||||
);
|
||||
}
|
||||
}
|
||||
const labelNames = Array.from(allLabelNames);
|
||||
const labelNames = Array.from(allLabelNames).map((labelName) =>
|
||||
this.sanitizeName(labelName),
|
||||
);
|
||||
|
||||
if (metric.type === 'counter') {
|
||||
let counter: Counter<string>;
|
||||
|
Loading…
Reference in New Issue
Block a user