mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
* Explore: Transform prometheus query to elasticsearch query Enable a way to transform prometheus/loki labels to elasticsearch query. This make a link between metrics to logs. Examples: A prometheus query : rate(my_metric{label1="value1",label2!="value2",label3=~"value.+",label4!~".*tothemoon"}[5m]) Will be this elasticsearch query when switching to elasticsearch datasource: __name__:"my_metric" AND label1:"value1" AND NOT label2:"value2" AND label3:/value.+/ AND NOT label4:/.*tothemoon/ * fix test * remove non needed async * Use prism token instead of regex * fix test ./scripts/ci-frontend-metrics.sh * mock timesrv and TemplateSrv in test * Remove unnecessary await/async Co-authored-by: Melchior MOULIN <m.moulin@criteo.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
128 lines
4.0 KiB
TypeScript
128 lines
4.0 KiB
TypeScript
import { ElasticsearchQuery } from './types';
|
|
import { DataQuery, LanguageProvider } from '@grafana/data';
|
|
|
|
import { ElasticDatasource } from './datasource';
|
|
|
|
import { PromQuery } from '../prometheus/types';
|
|
|
|
import Prism, { Token } from 'prismjs';
|
|
import grammar from '../prometheus/promql';
|
|
|
|
function getNameLabelValue(promQuery: string, tokens: any): string {
|
|
let nameLabelValue = '';
|
|
for (let prop in tokens) {
|
|
if (typeof tokens[prop] === 'string') {
|
|
nameLabelValue = tokens[prop] as string;
|
|
break;
|
|
}
|
|
}
|
|
return nameLabelValue;
|
|
}
|
|
|
|
function extractPrometheusLabels(promQuery: string): string[][] {
|
|
const labels: string[][] = [];
|
|
if (!promQuery || promQuery.length === 0) {
|
|
return labels;
|
|
}
|
|
const tokens = Prism.tokenize(promQuery, grammar);
|
|
const nameLabelValue = getNameLabelValue(promQuery, tokens);
|
|
if (nameLabelValue && nameLabelValue.length > 0) {
|
|
labels.push(['__name__', '=', '"' + nameLabelValue + '"']);
|
|
}
|
|
|
|
for (let prop in tokens) {
|
|
if (tokens[prop] instanceof Token) {
|
|
let token: Token = tokens[prop] as Token;
|
|
if (token.type === 'context-labels') {
|
|
let labelKey = '';
|
|
let labelValue = '';
|
|
let labelOperator = '';
|
|
let contentTokens: any[] = token.content as any[];
|
|
for (let currentToken in contentTokens) {
|
|
if (typeof contentTokens[currentToken] === 'string') {
|
|
let currentStr: string;
|
|
currentStr = contentTokens[currentToken] as string;
|
|
if (currentStr === '=' || currentStr === '!=' || currentStr === '=~' || currentStr === '!~') {
|
|
labelOperator = currentStr;
|
|
}
|
|
} else if (contentTokens[currentToken] instanceof Token) {
|
|
switch (contentTokens[currentToken].type) {
|
|
case 'label-key':
|
|
labelKey = contentTokens[currentToken].content as string;
|
|
break;
|
|
case 'label-value':
|
|
labelValue = contentTokens[currentToken].content as string;
|
|
labels.push([labelKey, labelOperator, labelValue]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return labels;
|
|
}
|
|
|
|
function getElasticsearchQuery(prometheusLabels: string[][]): string {
|
|
let elasticsearchLuceneLabels = [];
|
|
for (let keyOperatorValue of prometheusLabels) {
|
|
switch (keyOperatorValue[1]) {
|
|
case '=': {
|
|
elasticsearchLuceneLabels.push(keyOperatorValue[0] + ':' + keyOperatorValue[2]);
|
|
break;
|
|
}
|
|
case '!=': {
|
|
elasticsearchLuceneLabels.push('NOT ' + keyOperatorValue[0] + ':' + keyOperatorValue[2]);
|
|
break;
|
|
}
|
|
case '=~': {
|
|
elasticsearchLuceneLabels.push(
|
|
keyOperatorValue[0] + ':/' + keyOperatorValue[2].substring(1, keyOperatorValue[2].length - 1) + '/'
|
|
);
|
|
break;
|
|
}
|
|
case '!~': {
|
|
elasticsearchLuceneLabels.push(
|
|
'NOT ' + keyOperatorValue[0] + ':/' + keyOperatorValue[2].substring(1, keyOperatorValue[2].length - 1) + '/'
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return elasticsearchLuceneLabels.join(' AND ');
|
|
}
|
|
|
|
export default class ElasticsearchLanguageProvider extends LanguageProvider {
|
|
request: (url: string, params?: any) => Promise<any>;
|
|
start: () => Promise<any[]>;
|
|
datasource: ElasticDatasource;
|
|
|
|
constructor(datasource: ElasticDatasource, initialValues?: any) {
|
|
super();
|
|
this.datasource = datasource;
|
|
|
|
Object.assign(this, initialValues);
|
|
}
|
|
|
|
importQueries(queries: DataQuery[], datasourceType: string): ElasticsearchQuery[] {
|
|
if (datasourceType === 'prometheus' || datasourceType === 'loki') {
|
|
return queries.map(query => {
|
|
let prometheusQuery: PromQuery = query as PromQuery;
|
|
const expr = getElasticsearchQuery(extractPrometheusLabels(prometheusQuery.expr));
|
|
return {
|
|
isLogsQuery: true,
|
|
query: expr,
|
|
refId: query.refId,
|
|
};
|
|
});
|
|
}
|
|
return queries.map(query => {
|
|
return {
|
|
isLogsQuery: true,
|
|
query: '',
|
|
refId: query.refId,
|
|
};
|
|
});
|
|
}
|
|
}
|