The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/plugins/datasource/loki/getDerivedFields.ts

119 lines
4.2 KiB

import { groupBy } from 'lodash';
import { FieldType, DataFrame, DataLink, Field } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { DerivedFieldConfig } from './types';
export function getDerivedFields(dataFrame: DataFrame, derivedFieldConfigs: DerivedFieldConfig[]): Field[] {
if (!derivedFieldConfigs.length) {
return [];
}
const derivedFieldsGrouped = groupBy(derivedFieldConfigs, 'name');
const newFields = Object.values(derivedFieldsGrouped).map(fieldFromDerivedFieldConfig);
// line-field is the first string-field
// NOTE: we should create some common log-frame-extra-string-field code somewhere
const lineField = dataFrame.fields.find((f) => f.type === FieldType.string);
if (lineField === undefined) {
// if this is happening, something went wrong, let's raise an error
throw new Error('invalid logs-dataframe, string-field missing');
}
const labelFields = dataFrame.fields.find((f) => f.type === FieldType.other && f.name === 'labels');
for (let i = 0; i < lineField.values.length; i++) {
for (const field of newFields) {
// `matcherRegex` can be either a RegExp that is used to extract the value from the log line, or it can be a label key to derive the field from the labels
if (derivedFieldsGrouped[field.name][0].matcherType === 'label' && labelFields) {
const label = labelFields.values[i];
if (label) {
// Find the key that matches both, the `matcherRegex` and the label key
const intersectingKey = Object.keys(label).find(
(key) => derivedFieldsGrouped[field.name][0].matcherRegex === key
);
if (intersectingKey) {
field.values.push(label[intersectingKey]);
continue;
}
}
field.values.push(null);
} else if (
derivedFieldsGrouped[field.name][0].matcherType === 'regex' ||
derivedFieldsGrouped[field.name][0].matcherType === undefined
) {
// `matcherRegex` will actually be used as a RegExp here
const line = lineField.values[i];
const logMatch = line.match(derivedFieldsGrouped[field.name][0].matcherRegex);
if (logMatch && logMatch[1]) {
field.values.push(logMatch[1]);
continue;
}
field.values.push(null);
} else {
field.values.push(null);
}
}
}
return newFields;
}
/**
* Transform derivedField config into dataframe field with config that contains link.
*/
function fieldFromDerivedFieldConfig(derivedFieldConfigs: DerivedFieldConfig[]): Field {
const dataSourceSrv = getDataSourceSrv();
const dataLinks = derivedFieldConfigs.reduce<DataLink[]>((acc, derivedFieldConfig) => {
// Having field.datasourceUid means it is an internal link.
if (derivedFieldConfig.datasourceUid) {
const dsSettings = dataSourceSrv.getInstanceSettings(derivedFieldConfig.datasourceUid);
const queryType = (type: string | undefined): string | undefined => {
switch (type) {
case 'tempo':
return 'traceql';
case 'grafana-x-ray-datasource':
return 'getTrace';
default:
return undefined;
}
};
acc.push({
// Will be filled out later
title: derivedFieldConfig.urlDisplayLabel || '',
url: '',
// This is hardcoded for Jaeger or Zipkin not way right now to specify datasource specific query object
internal: {
query: { query: derivedFieldConfig.url, queryType: queryType(dsSettings?.type) },
datasourceUid: derivedFieldConfig.datasourceUid,
datasourceName: dsSettings?.name ?? 'Data source not found',
},
});
} else if (derivedFieldConfig.url) {
acc.push({
// We do not know what title to give here so we count on presentation layer to create a title from metadata.
title: derivedFieldConfig.urlDisplayLabel || '',
// This is hardcoded for Jaeger or Zipkin not way right now to specify datasource specific query object
url: derivedFieldConfig.url,
});
}
return acc;
}, []);
return {
name: derivedFieldConfigs[0].name,
type: FieldType.string,
config: {
links: dataLinks,
},
// We are adding values later on
values: [],
};
}