Commit a50cb6aa by Andrej Ocenas Committed by GitHub

Loki: Allow multiple derived fields with the same name (#24437)

parent 0ac006ed
...@@ -106,6 +106,10 @@ class UnThemedLogDetails extends PureComponent<Props> { ...@@ -106,6 +106,10 @@ class UnThemedLogDetails extends PureComponent<Props> {
); );
}); });
/**
* Returns all fields for log row which consists of fields we parse from the message itself and any derived fields
* setup in data source config.
*/
getAllFields = memoizeOne((row: LogRowModel) => { getAllFields = memoizeOne((row: LogRowModel) => {
const fields = this.parseMessage(row.entry); const fields = this.parseMessage(row.entry);
const derivedFields = this.getDerivedFields(row); const derivedFields = this.getDerivedFields(row);
...@@ -121,18 +125,10 @@ class UnThemedLogDetails extends PureComponent<Props> { ...@@ -121,18 +125,10 @@ class UnThemedLogDetails extends PureComponent<Props> {
} }
return acc; return acc;
}, {} as { [key: string]: FieldDef }); }, {} as { [key: string]: FieldDef });
const allFields = Object.values(fieldsMap);
allFields.sort((fieldA, fieldB) => {
if (fieldA.links?.length && !fieldB.links?.length) {
return -1;
}
if (!fieldA.links?.length && fieldB.links?.length) { const allFields = Object.values(fieldsMap);
return 1; allFields.sort(sortFieldsLinkFirst);
}
return fieldA.key > fieldB.key ? 1 : fieldA.key < fieldB.key ? -1 : 0;
});
return allFields; return allFields;
}); });
...@@ -233,5 +229,15 @@ class UnThemedLogDetails extends PureComponent<Props> { ...@@ -233,5 +229,15 @@ class UnThemedLogDetails extends PureComponent<Props> {
} }
} }
function sortFieldsLinkFirst(fieldA: FieldDef, fieldB: FieldDef) {
if (fieldA.links?.length && !fieldB.links?.length) {
return -1;
}
if (!fieldA.links?.length && fieldB.links?.length) {
return 1;
}
return fieldA.key > fieldB.key ? 1 : fieldA.key < fieldB.key ? -1 : 0;
}
export const LogDetails = withTheme(UnThemedLogDetails); export const LogDetails = withTheme(UnThemedLogDetails);
LogDetails.displayName = 'LogDetails'; LogDetails.displayName = 'LogDetails';
...@@ -5,7 +5,7 @@ import { serializeStateToUrlParam } from '../../../core/utils/explore'; ...@@ -5,7 +5,7 @@ import { serializeStateToUrlParam } from '../../../core/utils/explore';
import { getDataSourceSrv } from '@grafana/runtime'; import { getDataSourceSrv } from '@grafana/runtime';
/** /**
* Get links from the filed of a dataframe that was given to as and in addition check if there is associated * Get links from the field of a dataframe and in addition check if there is associated
* metadata with datasource in which case we will add onClick to open the link in new split window. This assumes * metadata with datasource in which case we will add onClick to open the link in new split window. This assumes
* that we just supply datasource name and field value and Explore split window will know how to render that * that we just supply datasource name and field value and Explore split window will know how to render that
* appropriately. This is for example used for transition from log with traceId to trace datasource to show that * appropriately. This is for example used for transition from log with traceId to trace datasource to show that
......
...@@ -131,6 +131,11 @@ describe('enhanceDataFrame', () => { ...@@ -131,6 +131,11 @@ describe('enhanceDataFrame', () => {
name: 'trace2', name: 'trace2',
datasourceUid: 'uid', datasourceUid: 'uid',
}, },
{
matcherRegex: 'trace2=(\\w+)',
name: 'trace2',
datasourceUid: 'uid2',
},
], ],
}); });
expect(df.fields.length).toBe(3); expect(df.fields.length).toBe(3);
...@@ -142,9 +147,14 @@ describe('enhanceDataFrame', () => { ...@@ -142,9 +147,14 @@ describe('enhanceDataFrame', () => {
}); });
expect(fc.getFieldByName('trace2').values.toArray()).toEqual([null, null, 'foo']); expect(fc.getFieldByName('trace2').values.toArray()).toEqual([null, null, 'foo']);
expect(fc.getFieldByName('trace2').config.links.length).toBe(2);
expect(fc.getFieldByName('trace2').config.links[0]).toEqual({ expect(fc.getFieldByName('trace2').config.links[0]).toEqual({
title: '', title: '',
meta: { datasourceUid: 'uid' }, meta: { datasourceUid: 'uid' },
}); });
expect(fc.getFieldByName('trace2').config.links[1]).toEqual({
title: '',
meta: { datasourceUid: 'uid2' },
});
}); });
}); });
...@@ -10,7 +10,6 @@ import { ...@@ -10,7 +10,6 @@ import {
ArrayVector, ArrayVector,
MutableDataFrame, MutableDataFrame,
findUniqueLabels, findUniqueLabels,
FieldConfig,
DataFrameView, DataFrameView,
DataLink, DataLink,
Field, Field,
...@@ -332,14 +331,15 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul ...@@ -332,14 +331,15 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul
if (!derivedFields.length) { if (!derivedFields.length) {
return; return;
} }
const newFields = derivedFields.map(fieldFromDerivedFieldConfig); const derivedFieldsGrouped = _.groupBy(derivedFields, 'name');
const newFieldsMap = _.keyBy(newFields, 'name');
const newFields = Object.values(derivedFieldsGrouped).map(fieldFromDerivedFieldConfig);
const view = new DataFrameView(dataFrame); const view = new DataFrameView(dataFrame);
view.forEach((row: { line: string }) => { view.forEach((row: { line: string }) => {
for (const field of derivedFields) { for (const field of newFields) {
const logMatch = row.line.match(field.matcherRegex); const logMatch = row.line.match(derivedFieldsGrouped[field.name][0].matcherRegex);
newFieldsMap[field.name].values.add(logMatch && logMatch[1]); field.values.add(logMatch && logMatch[1]);
} }
}); });
...@@ -349,28 +349,30 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul ...@@ -349,28 +349,30 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul
/** /**
* Transform derivedField config into dataframe field with config that contains link. * Transform derivedField config into dataframe field with config that contains link.
*/ */
function fieldFromDerivedFieldConfig(derivedFieldConfig: DerivedFieldConfig): Field<any, ArrayVector> { function fieldFromDerivedFieldConfig(derivedFieldConfigs: DerivedFieldConfig[]): Field<any, ArrayVector> {
const config: FieldConfig = {}; const dataLinks = derivedFieldConfigs.reduce((acc, derivedFieldConfig) => {
if (derivedFieldConfig.url || derivedFieldConfig.datasourceUid) { if (derivedFieldConfig.url || derivedFieldConfig.datasourceUid) {
const link: Partial<DataLink> = { acc.push({
// We do not know what title to give here so we count on presentation layer to create a title from metadata. // We do not know what title to give here so we count on presentation layer to create a title from metadata.
title: '', title: '',
url: derivedFieldConfig.url, url: derivedFieldConfig.url,
}; // Having field.datasourceUid means it is an internal link.
meta: derivedFieldConfig.datasourceUid
// Having field.datasourceUid means it is an internal link. ? {
if (derivedFieldConfig.datasourceUid) { datasourceUid: derivedFieldConfig.datasourceUid,
link.meta = { }
datasourceUid: derivedFieldConfig.datasourceUid, : undefined,
}; });
} }
return acc;
}, [] as DataLink[]);
config.links = [link as DataLink];
}
return { return {
name: derivedFieldConfig.name, name: derivedFieldConfigs[0].name,
type: FieldType.string, type: FieldType.string,
config, config: {
links: dataLinks,
},
// We are adding values later on // We are adding values later on
values: new ArrayVector<string>([]), values: new ArrayVector<string>([]),
}; };
......
...@@ -4,7 +4,7 @@ echo -e "Collecting code stats (typescript errors & more)" ...@@ -4,7 +4,7 @@ echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=728 ERROR_COUNT_LIMIT=726
DIRECTIVES_LIMIT=172 DIRECTIVES_LIMIT=172
CONTROLLERS_LIMIT=139 CONTROLLERS_LIMIT=139
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment