Loki: add additional settings section (#71035)

* Loki: add additional settings section

* Derived fields: add config subsection

* Query settings: add config subsection

* Loki config: use divider instead of hr

* Derived fields: refactor legacy styles

* Loki config: add divider between derived fields and query settings

* Loki config: create alerting settings for Loki
This commit is contained in:
Matias Chomicki 2023-07-06 10:20:38 +02:00 committed by GitHub
parent f6f3d97288
commit 1b80df0168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 173 additions and 89 deletions

View File

@ -0,0 +1,23 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { createDefaultConfigOptions } from '../mocks';
import { AlertingSettings } from './AlertingSettings';
const options = createDefaultConfigOptions();
describe('AlertingSettings', () => {
it('should render', () => {
render(<AlertingSettings options={options} onOptionsChange={() => {}} />);
expect(screen.getByText('Alerting')).toBeInTheDocument();
});
it('should update alerting settings', async () => {
const onChange = jest.fn();
render(<AlertingSettings options={options} onOptionsChange={onChange} />);
await userEvent.click(screen.getByLabelText('Toggle switch'));
expect(onChange).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,31 @@
import React from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { ConfigSubSection } from '@grafana/experimental';
import { InlineField, InlineSwitch } from '@grafana/ui';
export function AlertingSettings({
options,
onOptionsChange,
}: Pick<DataSourcePluginOptionsEditorProps, 'options' | 'onOptionsChange'>) {
return (
<ConfigSubSection title="Alerting">
<InlineField
labelWidth={29}
label="Manage alert rules in Alerting UI"
disabled={options.readOnly}
tooltip="Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager data source."
>
<InlineSwitch
value={options.jsonData.manageAlerts !== false}
onChange={(event) =>
onOptionsChange({
...options,
jsonData: { ...options.jsonData, manageAlerts: event!.currentTarget.checked },
})
}
/>
</InlineField>
</ConfigSubSection>
);
}

View File

@ -1,11 +1,14 @@
import React, { useCallback } from 'react';
import { DataSourcePluginOptionsEditorProps, DataSourceSettings } from '@grafana/data';
import { ConfigSection } from '@grafana/experimental';
import { config, reportInteraction } from '@grafana/runtime';
import { AlertingSettings, DataSourceHttpSettings } from '@grafana/ui';
import { DataSourceHttpSettings } from '@grafana/ui';
import { Divider } from 'app/core/components/Divider';
import { LokiOptions } from '../types';
import { AlertingSettings } from './AlertingSettings';
import { DerivedFields } from './DerivedFields';
import { QuerySettings } from './QuerySettings';
@ -40,6 +43,8 @@ export const ConfigEditor = (props: Props) => {
return (
<>
<Divider />
<DataSourceHttpSettings
defaultUrl={'http://localhost:3100'}
dataSourceConfig={options}
@ -48,19 +53,28 @@ export const ConfigEditor = (props: Props) => {
secureSocksDSProxyEnabled={config.secureSocksDSProxyEnabled}
/>
<AlertingSettings<LokiOptions> options={options} onOptionsChange={onOptionsChange} />
<Divider />
<QuerySettings
maxLines={options.jsonData.maxLines || ''}
onMaxLinedChange={(value) => onOptionsChange(setMaxLines(options, value))}
predefinedOperations={options.jsonData.predefinedOperations || ''}
onPredefinedOperationsChange={updatePredefinedOperations}
/>
<DerivedFields
fields={options.jsonData.derivedFields}
onChange={(value) => onOptionsChange(setDerivedFields(options, value))}
/>
<ConfigSection
title="Additional settings"
description="Additional settings are optional settings that can be configured for more control over your data source."
isCollapsible={true}
isInitiallyOpen
>
<AlertingSettings options={options} onOptionsChange={onOptionsChange} />
<Divider hideLine />
<QuerySettings
maxLines={options.jsonData.maxLines || ''}
onMaxLinedChange={(value) => onOptionsChange(setMaxLines(options, value))}
predefinedOperations={options.jsonData.predefinedOperations || ''}
onPredefinedOperationsChange={updatePredefinedOperations}
/>
<Divider hideLine />
<DerivedFields
fields={options.jsonData.derivedFields}
onChange={(value) => onOptionsChange(setDerivedFields(options, value))}
/>
</ConfigSection>
</>
);
};

View File

@ -2,7 +2,9 @@ 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';
@ -10,13 +12,18 @@ import { DebugSection } from './DebugSection';
import { DerivedField } from './DerivedField';
const getStyles = (theme: GrafanaTheme2) => ({
infoText: css`
padding-bottom: ${theme.spacing(2)};
color: ${theme.colors.text.secondary};
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 = {
@ -38,14 +45,17 @@ export const DerivedFields = ({ fields = [], onChange }: Props) => {
);
return (
<>
<h3 className="page-heading">Derived fields</h3>
<div className={styles.infoText}>
Derived fields can be used to extract new fields from a log message and create a link from its value.
</div>
<div className="gf-form-group">
<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-derived-fields"
feature="derived fields"
/>
}
>
<div className={styles.container}>
{fields.map((field, index) => {
return (
<DerivedField
@ -77,9 +87,7 @@ export const DerivedFields = ({ fields = [], onChange }: Props) => {
<div>
<Button
variant="secondary"
className={css`
margin-right: 10px;
`}
className={styles.addButton}
icon="plus"
onClick={(event) => {
event.preventDefault();
@ -96,18 +104,18 @@ export const DerivedFields = ({ fields = [], onChange }: Props) => {
</Button>
)}
</div>
</div>
{showDebug && (
<div className="gf-form-group">
<DebugSection
className={css`
margin-bottom: 10px;
`}
derivedFields={fields}
/>
</div>
)}
</>
{showDebug && (
<div className={styles.debugSection}>
<DebugSection
className={css`
margin-bottom: 10px;
`}
derivedFields={fields}
/>
</div>
)}
</div>
</ConfigSubSection>
);
};

View File

@ -1,7 +1,9 @@
import React from 'react';
import { ConfigSubSection } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { Badge, LegacyForms } from '@grafana/ui';
import { ConfigDescriptionLink } from 'app/core/components/ConfigDescriptionLink';
const { FormField } = LegacyForms;
@ -15,69 +17,75 @@ type Props = {
export const QuerySettings = (props: Props) => {
const { maxLines, onMaxLinedChange, predefinedOperations, onPredefinedOperationsChange } = props;
return (
<>
<h3 className="page-heading">Queries</h3>
<div className="gf-form-group">
<ConfigSubSection
title="Queries"
description={
<ConfigDescriptionLink
description="Additional options to customize your querying experience. "
suffix="loki/#configure-the-data-source"
feature="query settings"
/>
}
>
<div className="gf-form-inline">
<div className="gf-form">
<FormField
label="Maximum lines"
labelWidth={11}
inputWidth={20}
inputEl={
<input
type="number"
className="gf-form-input width-8 gf-form-input--has-help-icon"
value={maxLines}
onChange={(event) => onMaxLinedChange(event.currentTarget.value)}
spellCheck={false}
placeholder="1000"
/>
}
tooltip={
<>
Loki queries must contain a limit of the maximum number of lines returned (default: 1000). Increase this
limit to have a bigger result set for ad-hoc analysis. Decrease this limit if your browser becomes
sluggish when displaying the log results.
</>
}
/>
</div>
</div>
{config.featureToggles.lokiPredefinedOperations && (
<div className="gf-form-inline">
<div className="gf-form">
<FormField
label="Maximum lines"
label="Predefined operations"
labelWidth={11}
inputWidth={20}
inputEl={
<input
type="number"
className="gf-form-input width-8 gf-form-input--has-help-icon"
value={maxLines}
onChange={(event) => onMaxLinedChange(event.currentTarget.value)}
type="string"
className="gf-form-input width-20 gf-form-input--has-help-icon"
value={predefinedOperations}
onChange={(event) => onPredefinedOperationsChange(event.currentTarget.value)}
spellCheck={false}
placeholder="1000"
placeholder="| unpack | line_format"
/>
}
tooltip={
<>
Loki queries must contain a limit of the maximum number of lines returned (default: 1000). Increase
this limit to have a bigger result set for ad-hoc analysis. Decrease this limit if your browser
becomes sluggish when displaying the log results.
</>
<div>
{
'Predefined operations are used as an initial state for your queries. They are useful, if you want to unpack, parse or format all log lines. Currently we support only log operations starting with |. For example: | unpack | line_format "{{.message}}".'
}
</div>
}
/>
<Badge
text="Experimental"
color="orange"
icon="exclamation-triangle"
tooltip="Predefined operations is an experimental feature that may change in the future."
/>
</div>
</div>
{config.featureToggles.lokiPredefinedOperations && (
<div className="gf-form-inline">
<div className="gf-form">
<FormField
label="Predefined operations"
labelWidth={11}
inputEl={
<input
type="string"
className="gf-form-input width-20 gf-form-input--has-help-icon"
value={predefinedOperations}
onChange={(event) => onPredefinedOperationsChange(event.currentTarget.value)}
spellCheck={false}
placeholder="| unpack | line_format"
/>
}
tooltip={
<div>
{
'Predefined operations are used as an initial state for your queries. They are useful, if you want to unpack, parse or format all log lines. Currently we support only log operations starting with |. For example: | unpack | line_format "{{.message}}".'
}
</div>
}
/>
<Badge
text="Experimental"
color="orange"
icon="exclamation-triangle"
tooltip="Predefined operations is an experimental feature that may change in the future."
/>
</div>
</div>
)}
</div>
</>
)}
</ConfigSubSection>
);
};