mirror of https://github.com/grafana/grafana.git
properly detect histograms
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
This commit is contained in:
parent
3f064b9ff0
commit
5820dbc159
|
@ -0,0 +1,124 @@
|
|||
import { getQueriesForMetric, findMetadataForMetric } from './promQueries';
|
||||
|
||||
describe('promQueries', () => {
|
||||
const mockMetadata = {
|
||||
'http_request_duration': {
|
||||
type: 'histogram',
|
||||
help: 'Duration of HTTP requests',
|
||||
},
|
||||
'http_request_duration_bucket': {
|
||||
type: 'counter',
|
||||
help: 'Cumulative counters for the observation buckets (Duration of HTTP requests)',
|
||||
},
|
||||
'http_request_duration_count': {
|
||||
type: 'counter',
|
||||
help: 'Count of events that have been observed for the histogram metric (Duration of HTTP requests)',
|
||||
},
|
||||
'http_request_duration_sum': {
|
||||
type: 'counter',
|
||||
help: 'Total sum of all observed values for the histogram metric (Duration of HTTP requests)',
|
||||
},
|
||||
'cpu_usage': {
|
||||
type: 'gauge',
|
||||
help: 'CPU usage percentage',
|
||||
},
|
||||
'requests_total': {
|
||||
type: 'counter',
|
||||
help: 'Total number of requests',
|
||||
},
|
||||
};
|
||||
|
||||
describe('findMetadataForMetric', () => {
|
||||
it('should return histogram metadata for _bucket suffix', () => {
|
||||
const result = findMetadataForMetric('http_request_duration_bucket', mockMetadata);
|
||||
expect(result?.type).toBe('histogram');
|
||||
expect(result?.help).toBe('Duration of HTTP requests');
|
||||
});
|
||||
|
||||
it('should return histogram metadata for _count suffix', () => {
|
||||
const result = findMetadataForMetric('http_request_duration_count', mockMetadata);
|
||||
expect(result?.type).toBe('histogram');
|
||||
expect(result?.help).toBe('Duration of HTTP requests');
|
||||
});
|
||||
|
||||
it('should return histogram metadata for _sum suffix', () => {
|
||||
const result = findMetadataForMetric('http_request_duration_sum', mockMetadata);
|
||||
expect(result?.type).toBe('histogram');
|
||||
expect(result?.help).toBe('Duration of HTTP requests');
|
||||
});
|
||||
|
||||
it('should return exact match for non-histogram metrics', () => {
|
||||
const result = findMetadataForMetric('cpu_usage', mockMetadata);
|
||||
expect(result?.type).toBe('gauge');
|
||||
expect(result?.help).toBe('CPU usage percentage');
|
||||
});
|
||||
|
||||
it('should return counter metadata for counter metrics', () => {
|
||||
const result = findMetadataForMetric('requests_total', mockMetadata);
|
||||
expect(result?.type).toBe('counter');
|
||||
expect(result?.help).toBe('Total number of requests');
|
||||
});
|
||||
|
||||
it('should return undefined for non-existent metrics', () => {
|
||||
const result = findMetadataForMetric('non_existent_metric', mockMetadata);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getQueriesForMetric', () => {
|
||||
it('should return histogram queries for _bucket suffix metrics', () => {
|
||||
const result = getQueriesForMetric('http_request_duration_bucket', mockMetadata);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// Check that we get histogram-specific queries
|
||||
const percentileQuery = result.find(panel => panel.name === '95th percentile');
|
||||
expect(percentileQuery).toBeDefined();
|
||||
expect(percentileQuery?.targets[0].expr).toBe('histogram_quantile(0.95, rate(http_request_duration_bucket[5m]))');
|
||||
});
|
||||
|
||||
it('should return histogram queries for _count suffix metrics', () => {
|
||||
const result = getQueriesForMetric('http_request_duration_count', mockMetadata);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// Check that we get histogram-specific queries with base metric name
|
||||
const percentileQuery = result.find(panel => panel.name === '95th percentile');
|
||||
expect(percentileQuery).toBeDefined();
|
||||
expect(percentileQuery?.targets[0].expr).toBe('histogram_quantile(0.95, rate(http_request_duration_bucket[5m]))');
|
||||
});
|
||||
|
||||
it('should return histogram queries for _sum suffix metrics', () => {
|
||||
const result = getQueriesForMetric('http_request_duration_sum', mockMetadata);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// Check that we get histogram-specific queries with base metric name
|
||||
const averageQuery = result.find(panel => panel.name === 'Average');
|
||||
expect(averageQuery).toBeDefined();
|
||||
expect(averageQuery?.targets[0].expr).toBe('rate(http_request_duration_sum[5m]) / rate(http_request_duration_count[5m])');
|
||||
});
|
||||
|
||||
it('should return gauge queries for gauge metrics', () => {
|
||||
const result = getQueriesForMetric('cpu_usage', mockMetadata);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// Check that we get gauge-specific queries
|
||||
const currentValueQuery = result.find(panel => panel.name === 'Current value');
|
||||
expect(currentValueQuery).toBeDefined();
|
||||
expect(currentValueQuery?.targets[0].expr).toBe('cpu_usage');
|
||||
});
|
||||
|
||||
it('should return counter queries for counter metrics', () => {
|
||||
const result = getQueriesForMetric('requests_total', mockMetadata);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// Check that we get counter-specific queries
|
||||
const rateQuery = result.find(panel => panel.name === 'Rate per second');
|
||||
expect(rateQuery).toBeDefined();
|
||||
expect(rateQuery?.targets[0].expr).toBe('rate(requests_total[5m])');
|
||||
});
|
||||
|
||||
it('should return empty array for non-existent metrics', () => {
|
||||
const result = getQueriesForMetric('non_existent_metric', mockMetadata);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -274,13 +274,31 @@ function findMetadataForMetric(
|
|||
metricName: string,
|
||||
metricsMetadata: PromMetricsMetadata
|
||||
): PromMetricsMetadataItem | undefined {
|
||||
// First try exact match
|
||||
// For histogram/summary suffixes, prioritize finding the base metric first
|
||||
// This ensures that metrics like 'http_request_duration_bucket' return 'histogram' type
|
||||
// instead of the synthetic 'counter' type created for the suffixed metrics
|
||||
const histogramSummarySuffixes = ['_bucket', '_count', '_sum'];
|
||||
for (const suffix of histogramSummarySuffixes) {
|
||||
if (metricName.endsWith(suffix)) {
|
||||
const baseMetricName = metricName.slice(0, -suffix.length);
|
||||
if (metricsMetadata[baseMetricName]) {
|
||||
const baseMetadata = metricsMetadata[baseMetricName];
|
||||
// Only return base metadata if it's actually a histogram or summary
|
||||
if (baseMetadata.type === 'histogram' || baseMetadata.type === 'summary') {
|
||||
return baseMetadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try exact match for non-histogram/summary metrics or when base metric isn't found
|
||||
if (metricsMetadata[metricName]) {
|
||||
return metricsMetadata[metricName];
|
||||
}
|
||||
|
||||
// Try removing common Prometheus suffixes to find the base metric
|
||||
for (const suffix of PROMETHEUS_SUFFIXES) {
|
||||
// Try removing other common Prometheus suffixes to find the base metric
|
||||
const otherSuffixes = PROMETHEUS_SUFFIXES.filter(s => !histogramSummarySuffixes.includes(s));
|
||||
for (const suffix of otherSuffixes) {
|
||||
if (metricName.endsWith(suffix)) {
|
||||
const baseMetricName = metricName.slice(0, -suffix.length);
|
||||
if (metricsMetadata[baseMetricName]) {
|
||||
|
@ -309,12 +327,24 @@ export function getQueriesForMetric(metricName: string, metricsMetadata: PromMet
|
|||
return [];
|
||||
}
|
||||
|
||||
// For histogram/summary metrics with suffixes, use the base metric name in queries
|
||||
// since the query templates expect the base name (e.g., 'http_request_duration' not 'http_request_duration_bucket')
|
||||
let queryMetricName = metricName;
|
||||
if (metadata.type === 'histogram' || metadata.type === 'summary') {
|
||||
const histogramSummarySuffixes = ['_bucket', '_count', '_sum'];
|
||||
for (const suffix of histogramSummarySuffixes) {
|
||||
if (metricName.endsWith(suffix)) {
|
||||
queryMetricName = metricName.slice(0, -suffix.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return panels.map((panel) => ({
|
||||
...panel,
|
||||
// name: `${metricName} - ${panel.name}`,
|
||||
targets: panel.targets.map((target) => ({
|
||||
...target,
|
||||
expr: target.expr.replace(/\{\{metric_name\}\}/g, metricName),
|
||||
expr: target.expr.replace(/\{\{metric_name\}\}/g, queryMetricName),
|
||||
})),
|
||||
}));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue