Files
grafana/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx
Zodan Jodan 53758ad764 Loki: Option to add derived fields based on labels (#76162)
* Plugin: Deriving fields by name from parsed logs

Loki only derives fields by a regex matcher, this limits its usage when functions such as `line_formatter` is used on
top of the logs.
Some users already have logs parsed in json or logfmt structure which are detected as fields in loki.
This pull request allows the mapping between detected fields values and derived values by matching the fields' names.
Currently the feature is behind `lokiEnableNameMatcherOption` feature toggle.

* improve settings page to have a `fieldType`

* improve derived fields getter to use `matcherRegex`

* fix failing test

* rename feature toggle to `lokiDerivedFieldsFromLabels`

* added suggestions from review

* add empty config object

* remove feature flag

* fix width of select

* default to `regex` derived field

* fix failing test

---------

Co-authored-by: Sven Grossmann <svennergr@gmail.com>
2023-11-14 15:06:02 +01:00

129 lines
3.7 KiB
TypeScript

import { css } from '@emotion/css';
import React, { useCallback, useState } from 'react';
import { GrafanaTheme2, VariableOrigin, DataLinkBuiltInVars } from '@grafana/data';
import { ConfigSubSection } from '@grafana/experimental';
import { Button, useTheme2 } from '@grafana/ui';
import { ConfigDescriptionLink } from 'app/core/components/ConfigDescriptionLink';
import { DerivedFieldConfig } from '../types';
import { DebugSection } from './DebugSection';
import { DerivedField } from './DerivedField';
const getStyles = (theme: GrafanaTheme2) => ({
addButton: css`
margin-right: 10px;
`,
derivedField: css`
margin-bottom: ${theme.spacing(1)};
`,
container: css`
margin-bottom: ${theme.spacing(4)};
`,
debugSection: css`
margin-top: ${theme.spacing(4)};
`,
});
type Props = {
fields?: DerivedFieldConfig[];
onChange: (value: DerivedFieldConfig[]) => void;
};
export const DerivedFields = ({ fields = [], onChange }: Props) => {
const theme = useTheme2();
const styles = getStyles(theme);
const [showDebug, setShowDebug] = useState(false);
const validateName = useCallback(
(name: string) => {
return fields.filter((field) => field.name && field.name === name).length <= 1;
},
[fields]
);
return (
<ConfigSubSection
title="Derived fields"
description={
<ConfigDescriptionLink
description="Derived fields can be used to extract new fields from a log message and create a link from its value."
suffix="loki/configure-loki-data-source/#derived-fields"
feature="derived fields"
/>
}
>
<div className={styles.container}>
{fields.map((field, index) => {
return (
<DerivedField
className={styles.derivedField}
key={index}
value={field}
onChange={(newField) => {
const newDerivedFields = [...fields];
newDerivedFields.splice(index, 1, newField);
onChange(newDerivedFields);
}}
onDelete={() => {
const newDerivedFields = [...fields];
newDerivedFields.splice(index, 1);
onChange(newDerivedFields);
}}
validateName={validateName}
suggestions={[
{
value: DataLinkBuiltInVars.valueRaw,
label: 'Raw value',
documentation: 'Exact string captured by the regular expression',
origin: VariableOrigin.Value,
},
]}
/>
);
})}
<div>
<Button
variant="secondary"
className={styles.addButton}
icon="plus"
onClick={(event) => {
event.preventDefault();
const emptyConfig: DerivedFieldConfig = {
name: '',
matcherRegex: '',
urlDisplayLabel: '',
url: '',
matcherType: 'regex',
};
const newDerivedFields = [...fields, emptyConfig];
onChange(newDerivedFields);
}}
>
Add
</Button>
{fields.length > 0 && (
<Button variant="secondary" type="button" onClick={() => setShowDebug(!showDebug)}>
{showDebug ? 'Hide example log message' : 'Show example log message'}
</Button>
)}
</div>
{showDebug && (
<div className={styles.debugSection}>
<DebugSection
className={css`
margin-bottom: 10px;
`}
derivedFields={fields}
/>
</div>
)}
</div>
</ConfigSubSection>
);
};