mirror of
https://github.com/grafana/grafana.git
synced 2026-02-03 20:49:50 -05:00
chore(prom): add obfuscated query to tracking (#117165)
* chore(prom): add obfuscated query to tracking * Fix prettier * Fix * prettier
This commit is contained in:
parent
aefcbed25d
commit
2074778af5
2 changed files with 128 additions and 0 deletions
71
packages/grafana-prometheus/src/tracking.test.ts
Normal file
71
packages/grafana-prometheus/src/tracking.test.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { obfuscate } from './tracking';
|
||||
|
||||
describe('obfuscate', () => {
|
||||
it('obfuscates metric name', () => {
|
||||
expect(obfuscate('http_requests_total')).toEqual('Identifier');
|
||||
});
|
||||
|
||||
it('obfuscates metric name with labels', () => {
|
||||
expect(obfuscate('http_requests_total{job="grafana"}')).toEqual('Identifier{LabelName=StringLiteral}');
|
||||
});
|
||||
|
||||
it('obfuscates on valid query with rate', () => {
|
||||
expect(obfuscate('rate(http_requests_total{job="grafana"}[5m])')).toEqual(
|
||||
'rate(Identifier{LabelName=StringLiteral}[NumberDurationLiteral])'
|
||||
);
|
||||
});
|
||||
|
||||
it('obfuscates aggregation query', () => {
|
||||
expect(obfuscate('sum(rate(http_requests_total{job="grafana"}[5m])) by (instance)')).toEqual(
|
||||
'sum(rate(Identifier{LabelName=StringLiteral}[NumberDurationLiteral])) by (LabelName)'
|
||||
);
|
||||
});
|
||||
|
||||
it('obfuscates arithmetic operations', () => {
|
||||
expect(obfuscate('2 + 3')).toEqual('NumberDurationLiteral + NumberDurationLiteral');
|
||||
});
|
||||
|
||||
it('obfuscates binary operations with metrics', () => {
|
||||
expect(obfuscate('http_requests_total / http_requests_failed')).toEqual('Identifier / Identifier');
|
||||
});
|
||||
|
||||
it('obfuscates query with multiple labels', () => {
|
||||
expect(obfuscate('up{job="prometheus", instance="localhost:9090"}')).toEqual(
|
||||
'Identifier{LabelName=StringLiteral, LabelName=StringLiteral}'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not obfuscate __name__ label', () => {
|
||||
expect(obfuscate('{__name__="http_requests_total"}')).toEqual('{__name__=StringLiteral}');
|
||||
});
|
||||
|
||||
it('does not obfuscate interval variables', () => {
|
||||
expect(obfuscate('rate(http_requests_total[$__interval])')).toEqual('rate(Identifier[$__interval])');
|
||||
});
|
||||
|
||||
it('does not obfuscate rate_interval variable', () => {
|
||||
expect(obfuscate('rate(http_requests_total[$__rate_interval])')).toEqual('rate(Identifier[$__rate_interval])');
|
||||
});
|
||||
|
||||
it('does not obfuscate range variables', () => {
|
||||
expect(obfuscate('rate(http_requests_total[$__range])')).toEqual('rate(Identifier[$__range])');
|
||||
expect(obfuscate('rate(http_requests_total[$__range_s])')).toEqual('rate(Identifier[$__range_s])');
|
||||
expect(obfuscate('rate(http_requests_total[$__range_ms])')).toEqual('rate(Identifier[$__range_ms])');
|
||||
});
|
||||
|
||||
it('obfuscates complex query with histogram_quantile', () => {
|
||||
expect(
|
||||
obfuscate('histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="api"}[5m])) by (le))')
|
||||
).toEqual(
|
||||
'histogram_quantile(NumberDurationLiteral, sum(rate(Identifier{LabelName=StringLiteral}[NumberDurationLiteral])) by (LabelName))'
|
||||
);
|
||||
});
|
||||
|
||||
it('obfuscates offset modifier', () => {
|
||||
expect(obfuscate('http_requests_total offset 5m')).toEqual('Identifier offset NumberDurationLiteral');
|
||||
});
|
||||
|
||||
it('handles empty query', () => {
|
||||
expect(obfuscate('')).toEqual('');
|
||||
});
|
||||
});
|
||||
|
|
@ -1,9 +1,65 @@
|
|||
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/tracking.ts
|
||||
import {
|
||||
Identifier,
|
||||
LabelName,
|
||||
NumberDurationLiteral,
|
||||
NumberDurationLiteralInDurationContext,
|
||||
parser,
|
||||
StringLiteral,
|
||||
} from '@prometheus-io/lezer-promql';
|
||||
|
||||
import { CoreApp, DataQueryRequest, DataQueryResponse } from '@grafana/data';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import { PromQuery } from './types';
|
||||
|
||||
const tagsToObscure = [
|
||||
StringLiteral,
|
||||
Identifier,
|
||||
LabelName,
|
||||
NumberDurationLiteral,
|
||||
NumberDurationLiteralInDurationContext,
|
||||
];
|
||||
const partsToKeep = [
|
||||
'__name__',
|
||||
'__interval',
|
||||
'__interval_ms',
|
||||
'__rate_interval',
|
||||
'__range',
|
||||
'__range_s',
|
||||
'__range_ms',
|
||||
];
|
||||
|
||||
export function obfuscate(query: string): string {
|
||||
const replacements: Array<{ from: number; to: number; replacement: string }> = [];
|
||||
const tree = parser.parse(query);
|
||||
tree.iterate({
|
||||
enter: ({ type, from, to }): false | void => {
|
||||
const queryPart = query.substring(from, to);
|
||||
// Skip empty parts, parts to keep, and Grafana variable syntax
|
||||
if (
|
||||
queryPart.length === 0 ||
|
||||
partsToKeep.includes(queryPart) ||
|
||||
queryPart.startsWith('$__') ||
|
||||
!tagsToObscure.includes(type.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Use consistent name for duration literals
|
||||
const replacement = type.id === NumberDurationLiteralInDurationContext ? 'NumberDurationLiteral' : type.name;
|
||||
replacements.push({ from, to, replacement });
|
||||
},
|
||||
});
|
||||
|
||||
// Apply replacements from end to start to preserve positions
|
||||
replacements.sort((a, b) => b.from - a.from);
|
||||
let obfuscatedQuery = query;
|
||||
for (const { from, to, replacement } of replacements) {
|
||||
obfuscatedQuery = obfuscatedQuery.substring(0, from) + replacement + obfuscatedQuery.substring(to);
|
||||
}
|
||||
return obfuscatedQuery;
|
||||
}
|
||||
|
||||
export function trackQuery(
|
||||
response: DataQueryResponse,
|
||||
request: DataQueryRequest<PromQuery> & { targets: PromQuery[] },
|
||||
|
|
@ -24,6 +80,7 @@ export function trackQuery(
|
|||
has_data: response.data.some((frame) => frame.length > 0),
|
||||
has_error: response.error !== undefined,
|
||||
expr: query.expr,
|
||||
obfuscated_query: obfuscate(query.expr),
|
||||
format: query.format,
|
||||
instant: query.instant,
|
||||
range: query.range,
|
||||
|
|
|
|||
Loading…
Reference in a new issue