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> {
);
});
/**
* 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) => {
const fields = this.parseMessage(row.entry);
const derivedFields = this.getDerivedFields(row);
......@@ -121,18 +125,10 @@ class UnThemedLogDetails extends PureComponent<Props> {
}
return acc;
}, {} 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) {
return 1;
}
const allFields = Object.values(fieldsMap);
allFields.sort(sortFieldsLinkFirst);
return fieldA.key > fieldB.key ? 1 : fieldA.key < fieldB.key ? -1 : 0;
});
return allFields;
});
......@@ -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);
LogDetails.displayName = 'LogDetails';
......@@ -5,7 +5,7 @@ import { serializeStateToUrlParam } from '../../../core/utils/explore';
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
* 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
......
......@@ -131,6 +131,11 @@ describe('enhanceDataFrame', () => {
name: 'trace2',
datasourceUid: 'uid',
},
{
matcherRegex: 'trace2=(\\w+)',
name: 'trace2',
datasourceUid: 'uid2',
},
],
});
expect(df.fields.length).toBe(3);
......@@ -142,9 +147,14 @@ describe('enhanceDataFrame', () => {
});
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({
title: '',
meta: { datasourceUid: 'uid' },
});
expect(fc.getFieldByName('trace2').config.links[1]).toEqual({
title: '',
meta: { datasourceUid: 'uid2' },
});
});
});
......@@ -10,7 +10,6 @@ import {
ArrayVector,
MutableDataFrame,
findUniqueLabels,
FieldConfig,
DataFrameView,
DataLink,
Field,
......@@ -332,14 +331,15 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul
if (!derivedFields.length) {
return;
}
const newFields = derivedFields.map(fieldFromDerivedFieldConfig);
const newFieldsMap = _.keyBy(newFields, 'name');
const derivedFieldsGrouped = _.groupBy(derivedFields, 'name');
const newFields = Object.values(derivedFieldsGrouped).map(fieldFromDerivedFieldConfig);
const view = new DataFrameView(dataFrame);
view.forEach((row: { line: string }) => {
for (const field of derivedFields) {
const logMatch = row.line.match(field.matcherRegex);
newFieldsMap[field.name].values.add(logMatch && logMatch[1]);
for (const field of newFields) {
const logMatch = row.line.match(derivedFieldsGrouped[field.name][0].matcherRegex);
field.values.add(logMatch && logMatch[1]);
}
});
......@@ -349,28 +349,30 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul
/**
* Transform derivedField config into dataframe field with config that contains link.
*/
function fieldFromDerivedFieldConfig(derivedFieldConfig: DerivedFieldConfig): Field<any, ArrayVector> {
const config: FieldConfig = {};
if (derivedFieldConfig.url || derivedFieldConfig.datasourceUid) {
const link: Partial<DataLink> = {
// We do not know what title to give here so we count on presentation layer to create a title from metadata.
title: '',
url: derivedFieldConfig.url,
};
// Having field.datasourceUid means it is an internal link.
if (derivedFieldConfig.datasourceUid) {
link.meta = {
datasourceUid: derivedFieldConfig.datasourceUid,
};
function fieldFromDerivedFieldConfig(derivedFieldConfigs: DerivedFieldConfig[]): Field<any, ArrayVector> {
const dataLinks = derivedFieldConfigs.reduce((acc, derivedFieldConfig) => {
if (derivedFieldConfig.url || derivedFieldConfig.datasourceUid) {
acc.push({
// We do not know what title to give here so we count on presentation layer to create a title from metadata.
title: '',
url: derivedFieldConfig.url,
// Having field.datasourceUid means it is an internal link.
meta: derivedFieldConfig.datasourceUid
? {
datasourceUid: derivedFieldConfig.datasourceUid,
}
: undefined,
});
}
return acc;
}, [] as DataLink[]);
config.links = [link as DataLink];
}
return {
name: derivedFieldConfig.name,
name: derivedFieldConfigs[0].name,
type: FieldType.string,
config,
config: {
links: dataLinks,
},
// We are adding values later on
values: new ArrayVector<string>([]),
};
......
......@@ -4,7 +4,7 @@ echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=728
ERROR_COUNT_LIMIT=726
DIRECTIVES_LIMIT=172
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