Elasticsearch: Fix passing of limit and datalinks to logs data frame (#68554)

* Elasticsearch: Fix passing of limit and datalinks to logs data frame

* Update public/app/core/logsModel.ts

Co-authored-by: François Massot <francois.massot@gmail.com>

---------

Co-authored-by: François Massot <francois.massot@gmail.com>
This commit is contained in:
Ivana Huckova 2023-05-17 14:28:32 +02:00 committed by GitHub
parent d20a03e2d1
commit dbbbc46351
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 22 deletions

View File

@ -160,7 +160,7 @@ func processLogsResponse(res *es.SearchResponse, target *Query, configuredFields
frames := data.Frames{} frames := data.Frames{}
frame := data.NewFrame("", fields...) frame := data.NewFrame("", fields...)
setPreferredVisType(frame, data.VisTypeLogs) setPreferredVisType(frame, data.VisTypeLogs)
setSearchWords(frame, searchWords) setLogsCustomMeta(frame, searchWords, stringToIntWithDefaultValue(target.Metrics[0].Settings.Get("limit").MustString(), defaultSize))
frames = append(frames, frame) frames = append(frames, frame)
queryRes.Frames = frames queryRes.Frames = frames
@ -1137,7 +1137,7 @@ func setPreferredVisType(frame *data.Frame, visType data.VisType) {
frame.Meta.PreferredVisualization = visType frame.Meta.PreferredVisualization = visType
} }
func setSearchWords(frame *data.Frame, searchWords map[string]bool) { func setLogsCustomMeta(frame *data.Frame, searchWords map[string]bool, limit int) {
i := 0 i := 0
searchWordsList := make([]string, len(searchWords)) searchWordsList := make([]string, len(searchWords))
for searchWord := range searchWords { for searchWord := range searchWords {
@ -1156,6 +1156,7 @@ func setSearchWords(frame *data.Frame, searchWords map[string]bool) {
frame.Meta.Custom = map[string]interface{}{ frame.Meta.Custom = map[string]interface{}{
"searchWords": searchWordsList, "searchWords": searchWordsList,
"limit": limit,
} }
} }

View File

@ -105,7 +105,7 @@ func TestProcessLogsResponse(t *testing.T) {
logsFrame := frames[0] logsFrame := frames[0]
meta := logsFrame.Meta meta := logsFrame.Meta
require.Equal(t, map[string]interface{}{"searchWords": []string{"hello", "message"}}, meta.Custom) require.Equal(t, map[string]interface{}{"searchWords": []string{"hello", "message"}, "limit": 500}, meta.Custom)
require.Equal(t, data.VisTypeLogs, string(meta.PreferredVisualization)) require.Equal(t, data.VisTypeLogs, string(meta.PreferredVisualization))
logsFieldMap := make(map[string]*data.Field) logsFieldMap := make(map[string]*data.Field)
@ -430,6 +430,7 @@ func TestProcessLogsResponse(t *testing.T) {
require.Equal(t, map[string]interface{}{ require.Equal(t, map[string]interface{}{
"searchWords": []string{"hello", "message"}, "searchWords": []string{"hello", "message"},
"limit": 500,
}, customMeta) }, customMeta)
}) })
} }

View File

@ -6,6 +6,7 @@
// 0 // 0
// ], // ],
// "custom": { // "custom": {
// "limit": 500,
// "searchWords": [ // "searchWords": [
// "hello", // "hello",
// "message" // "message"
@ -40,6 +41,7 @@
0 0
], ],
"custom": { "custom": {
"limit": 500,
"searchWords": [ "searchWords": [
"hello", "hello",
"message" "message"

View File

@ -354,6 +354,48 @@ describe('dataFrameToLogsModel', () => {
}); });
}); });
it('given one series with limit as custom meta property should return correct limit', () => {
const series: DataFrame[] = [
new MutableDataFrame({
fields: [
{
name: 'time',
type: FieldType.time,
values: ['2019-04-26T09:28:11.352440161Z', '2019-04-26T14:42:50.991981292Z'],
},
{
name: 'message',
type: FieldType.string,
values: [
't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server',
't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
],
labels: {
filename: '/var/log/grafana/grafana.log',
job: 'grafana',
},
},
{
name: 'id',
type: FieldType.string,
values: ['foo', 'bar'],
},
],
meta: {
custom: {
limit: 1000,
},
},
}),
];
const logsModel = dataFrameToLogsModel(series, 1);
expect(logsModel.meta![1]).toMatchObject({
label: LIMIT_LABEL,
value: `1000 (2 returned)`,
kind: LogsMetaKind.String,
});
});
it('given one series with labels-field should return expected logs model', () => { it('given one series with labels-field should return expected logs model', () => {
const series: DataFrame[] = [ const series: DataFrame[] = [
new MutableDataFrame({ new MutableDataFrame({

View File

@ -474,10 +474,11 @@ export function logSeriesToLogsModel(logSeries: DataFrame[], queries: DataQuery[
kind: LogsMetaKind.LabelsMap, kind: LogsMetaKind.LabelsMap,
}); });
} }
// Data sources that set up searchWords on backend use meta.custom.limit.
const limits = logSeries.filter((series) => series.meta && series.meta.limit); // Data sources that set up searchWords through frontend can use meta.limit.
const limits = logSeries.filter((series) => series?.meta?.custom?.limit ?? series?.meta?.limit);
const lastLimitPerRef = limits.reduce<Record<string, number>>((acc, elem) => { const lastLimitPerRef = limits.reduce<Record<string, number>>((acc, elem) => {
acc[elem.refId ?? ''] = elem.meta?.limit ?? 0; acc[elem.refId ?? ''] = elem.meta?.custom?.limit ?? elem.meta?.limit ?? 0;
return acc; return acc;
}, {}); }, {});

View File

@ -15,10 +15,10 @@ import {
import { BackendSrvRequest, getBackendSrv, TemplateSrv } from '@grafana/runtime'; import { BackendSrvRequest, getBackendSrv, TemplateSrv } from '@grafana/runtime';
import { ElasticResponse } from './ElasticResponse'; import { ElasticResponse } from './ElasticResponse';
import { ElasticDatasource, enhanceDataFrame } from './datasource'; import { ElasticDatasource, enhanceDataFrameWithDataLinks } from './datasource';
import { defaultBucketAgg, hasMetricOfType } from './queryDef'; import { defaultBucketAgg, hasMetricOfType } from './queryDef';
import { trackQuery } from './tracking'; import { trackQuery } from './tracking';
import { ElasticsearchQuery, Logs } from './types'; import { DataLinkConfig, ElasticsearchQuery, Logs } from './types';
export class LegacyQueryRunner { export class LegacyQueryRunner {
datasource: ElasticDatasource; datasource: ElasticDatasource;
@ -250,3 +250,17 @@ function transformHitsBasedOnDirection(response: any, direction: 'asc' | 'desc')
], ],
}; };
} }
/**
* Modifies dataFrame and adds dataLinks from the config.
* Exported for tests.
*/
export function enhanceDataFrame(dataFrame: DataFrame, dataLinks: DataLinkConfig[], limit?: number) {
if (limit) {
dataFrame.meta = {
...dataFrame.meta,
limit,
};
}
enhanceDataFrameWithDataLinks(dataFrame, dataLinks);
}

View File

@ -25,7 +25,8 @@ import { TemplateSrv } from 'app/features/templating/template_srv';
import { createFetchResponse } from '../../../../test/helpers/createFetchResponse'; import { createFetchResponse } from '../../../../test/helpers/createFetchResponse';
import { ElasticDatasource, enhanceDataFrame } from './datasource'; import { enhanceDataFrame } from './LegacyQueryRunner';
import { ElasticDatasource } from './datasource';
import { createElasticDatasource } from './mocks'; import { createElasticDatasource } from './mocks';
import { Filters, ElasticsearchOptions, ElasticsearchQuery } from './types'; import { Filters, ElasticsearchOptions, ElasticsearchQuery } from './types';

View File

@ -626,7 +626,15 @@ export class ElasticDatasource
const { enableElasticsearchBackendQuerying } = config.featureToggles; const { enableElasticsearchBackendQuerying } = config.featureToggles;
if (enableElasticsearchBackendQuerying) { if (enableElasticsearchBackendQuerying) {
const start = new Date(); const start = new Date();
return super.query(request).pipe(tap((response) => trackQuery(response, request, start))); return super.query(request).pipe(
tap((response) => trackQuery(response, request, start)),
map((response) => {
response.data.forEach((dataFrame) => {
enhanceDataFrameWithDataLinks(dataFrame, this.dataLinks);
});
return response;
})
);
} }
return this.legacyQueryRunner.query(request); return this.legacyQueryRunner.query(request);
} }
@ -1034,18 +1042,7 @@ export class ElasticDatasource
}; };
} }
/** export function enhanceDataFrameWithDataLinks(dataFrame: DataFrame, dataLinks: DataLinkConfig[]) {
* Modifies dataframe and adds dataLinks from the config.
* Exported for tests.
*/
export function enhanceDataFrame(dataFrame: DataFrame, dataLinks: DataLinkConfig[], limit?: number) {
if (limit) {
dataFrame.meta = {
...dataFrame.meta,
limit,
};
}
if (!dataLinks.length) { if (!dataLinks.length) {
return; return;
} }