From 00e06874e5894f6a51d6376f1dc8ce064a588269 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 14 Dec 2021 19:19:16 -0800 Subject: [PATCH] Legend: extract common renderLegendFormat function (loki+prom) (#43054) --- .../app/plugins/datasource/loki/datasource.ts | 15 ++-------- .../datasource/loki/result_transformer.ts | 8 ++--- .../datasource/prometheus/datasource.ts | 7 +++-- .../datasource/prometheus/legend.test.ts | 30 +++++++++++++++++++ .../plugins/datasource/prometheus/legend.ts | 7 +++++ .../prometheus/result_transformer.ts | 13 ++------ 6 files changed, 48 insertions(+), 32 deletions(-) create mode 100644 public/app/plugins/datasource/prometheus/legend.test.ts create mode 100644 public/app/plugins/datasource/prometheus/legend.ts diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index a1cee05e48b..f345bf4c32f 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -62,6 +62,7 @@ import syntax from './syntax'; import { DEFAULT_RESOLUTION } from './components/LokiOptionFields'; import { queryLogsVolume } from 'app/core/logs_model'; import config from 'app/core/config'; +import { renderLegendFormat } from '../prometheus/legend'; export type RangeQueryOptions = DataQueryRequest | AnnotationQueryRequest; export const DEFAULT_MAX_LINES = 1000; @@ -684,8 +685,8 @@ export class LokiDatasource view.forEach((row) => { annotations.push({ time: new Date(row.ts).valueOf(), - title: renderTemplate(titleFormat, labels), - text: renderTemplate(textFormat, labels) || row.line, + title: renderLegendFormat(titleFormat, labels), + text: renderLegendFormat(textFormat, labels) || row.line, tags, }); }); @@ -752,16 +753,6 @@ export class LokiDatasource } } -export function renderTemplate(aliasPattern: string, aliasData: { [key: string]: string }) { - const aliasRegex = /\{\{\s*(.+?)\s*\}\}/g; - return aliasPattern.replace(aliasRegex, (_match, g1) => { - if (aliasData[g1]) { - return aliasData[g1]; - } - return ''; - }); -} - export function lokiRegularEscape(value: any) { if (typeof value === 'string') { return value.replace(/'/g, "\\\\'"); diff --git a/public/app/plugins/datasource/loki/result_transformer.ts b/public/app/plugins/datasource/loki/result_transformer.ts index 817ee1066d2..970cb1ddc45 100644 --- a/public/app/plugins/datasource/loki/result_transformer.ts +++ b/public/app/plugins/datasource/loki/result_transformer.ts @@ -37,6 +37,7 @@ import { LokiStreamResponse, LokiStats, } from './types'; +import { renderLegendFormat } from '../prometheus/legend'; const UUID_NAMESPACE = '6ec946da-0f49-47a8-983a-1d76d17e7c92'; @@ -282,7 +283,7 @@ export function createMetricLabel(labelData: { [key: string]: string }, options? let label = options === undefined || isEmpty(options.legendFormat) ? getOriginalMetricName(labelData) - : renderTemplate(getTemplateSrv().replace(options.legendFormat ?? '', options.scopedVars), labelData); + : renderLegendFormat(getTemplateSrv().replace(options.legendFormat ?? '', options.scopedVars), labelData); if (!label && options) { label = options.query; @@ -290,11 +291,6 @@ export function createMetricLabel(labelData: { [key: string]: string }, options? return label; } -function renderTemplate(aliasPattern: string, aliasData: { [key: string]: string }) { - const aliasRegex = /\{\{\s*(.+?)\s*\}\}/g; - return aliasPattern.replace(aliasRegex, (_, g1) => (aliasData[g1] ? aliasData[g1] : g1)); -} - function getOriginalMetricName(labelData: { [key: string]: string }) { const metricName = labelData.__name__ || ''; delete labelData.__name__; diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 021345e48bd..c2fdf84c1fa 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -38,7 +38,7 @@ import addLabelToQuery from './add_label_to_query'; import PrometheusLanguageProvider from './language_provider'; import { expandRecordingRules } from './language_utils'; import { getInitHints, getQueryHints } from './query_hints'; -import { getOriginalMetricName, renderTemplate, transform, transformV2 } from './result_transformer'; +import { getOriginalMetricName, transform, transformV2 } from './result_transformer'; import { ExemplarTraceIdDestination, PromDataErrorResponse, @@ -54,6 +54,7 @@ import { } from './types'; import { PrometheusVariableSupport } from './variables'; import PrometheusMetricFindQuery from './metric_find_query'; +import { renderLegendFormat } from './legend'; export const ANNOTATION_QUERY_STEP_DEFAULT = '60s'; const GET_AND_POST_METADATA_ENDPOINTS = ['api/v1/query', 'api/v1/query_range', 'api/v1/series', 'api/v1/labels']; @@ -765,9 +766,9 @@ export class PrometheusDatasource time: timestamp, timeEnd: timestamp, annotation, - title: renderTemplate(titleFormat, labels), + title: renderLegendFormat(titleFormat, labels), tags, - text: renderTemplate(textFormat, labels), + text: renderLegendFormat(textFormat, labels), }; } diff --git a/public/app/plugins/datasource/prometheus/legend.test.ts b/public/app/plugins/datasource/prometheus/legend.test.ts new file mode 100644 index 00000000000..65afea1a7ac --- /dev/null +++ b/public/app/plugins/datasource/prometheus/legend.test.ts @@ -0,0 +1,30 @@ +import { renderLegendFormat } from './legend'; + +describe('renderLegendFormat()', () => { + const labels = { + a: 'AAA', + b: 'BBB', + 'with space': 'CCC', + }; + + it('works without any labels', () => { + expect(renderLegendFormat('hello', {})).toEqual('hello'); + expect(renderLegendFormat('hello', labels)).toEqual('hello'); + }); + + it('Simple replace', () => { + expect(renderLegendFormat('value: {{a}}', labels)).toEqual('value: AAA'); + expect(renderLegendFormat('{{a}} {{with space}}', labels)).toEqual('AAA CCC'); + + // not sure if this is expected... but current behavior + expect(renderLegendFormat('{{ a }}', labels)).toEqual('AAA'); + }); + + it('Bad syntax', () => { + expect(renderLegendFormat('value: {{a}', labels)).toEqual('value: {{a}'); + expect(renderLegendFormat('value: {a}}}', labels)).toEqual('value: {a}}}'); + + // Current behavior -- not sure if expected or not + expect(renderLegendFormat('value: {{{a}}}', labels)).toEqual('value: {a}'); + }); +}); diff --git a/public/app/plugins/datasource/prometheus/legend.ts b/public/app/plugins/datasource/prometheus/legend.ts new file mode 100644 index 00000000000..57f73b62aa0 --- /dev/null +++ b/public/app/plugins/datasource/prometheus/legend.ts @@ -0,0 +1,7 @@ +import { Labels } from '@grafana/data'; + +/** replace labels in a string. Used for loki+prometheus legend formats */ +export function renderLegendFormat(aliasPattern: string, aliasData: Labels): string { + const aliasRegex = /\{\{\s*(.+?)\s*\}\}/g; + return aliasPattern.replace(aliasRegex, (_, g1) => (aliasData[g1] ? aliasData[g1] : g1)); +} diff --git a/public/app/plugins/datasource/prometheus/result_transformer.ts b/public/app/plugins/datasource/prometheus/result_transformer.ts index ea66c9ede79..d6244c959b7 100644 --- a/public/app/plugins/datasource/prometheus/result_transformer.ts +++ b/public/app/plugins/datasource/prometheus/result_transformer.ts @@ -33,6 +33,7 @@ import { PromValue, TransformOptions, } from './types'; +import { renderLegendFormat } from './legend'; const POSITIVE_INFINITY_SAMPLE_VALUE = '+Inf'; const NEGATIVE_INFINITY_SAMPLE_VALUE = '-Inf'; @@ -504,7 +505,7 @@ function getValueField({ function createLabelInfo(labels: { [key: string]: string }, options: TransformOptions) { if (options?.legendFormat) { - const title = renderTemplate(getTemplateSrv().replace(options.legendFormat, options?.scopedVars), labels); + const title = renderLegendFormat(getTemplateSrv().replace(options.legendFormat, options?.scopedVars), labels); return { name: title, labels }; } @@ -528,16 +529,6 @@ export function getOriginalMetricName(labelData: { [key: string]: string }) { return `${metricName}{${labelPart}}`; } -export function renderTemplate(aliasPattern: string, aliasData: { [key: string]: string }) { - const aliasRegex = /\{\{\s*(.+?)\s*\}\}/g; - return aliasPattern.replace(aliasRegex, (_match, g1) => { - if (aliasData[g1]) { - return aliasData[g1]; - } - return ''; - }); -} - function transformToHistogramOverTime(seriesList: DataFrame[]) { /* t1 = timestamp1, t2 = timestamp2 etc. t1 t2 t3 t1 t2 t3