Commit 83329ae7 by Ivana Huckova Committed by GitHub

Explore/Logs: Update Parsed fields to Detected fields (#28881)

* Change all parsed fields and functions to detected fields

* Update

* Sneak in UI improvement, add tooltip

* Update docs/sources/whatsnew/whats-new-in-v6-5.md

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

* Update docs/sources/whatsnew/whats-new-in-v6-5.md

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

* Remove changes from old Whats new

* Rename LogMessageParsed

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
parent 7e218639
......@@ -39,7 +39,7 @@ The Derived Fields configuration allows you to:
- Add fields parsed from the log message.
- Add a link that uses the value of the field.
You can use this functionality to link to your tracing backend directly from your logs, or link to a user profile page if a userId is present in the log line. These links appear in the [log details]({{< relref "../explore/#labels-and-parsed-fields" >}}).
You can use this functionality to link to your tracing backend directly from your logs, or link to a user profile page if a userId is present in the log line. These links appear in the [log details](/explore/#labels-and-detected-fields).
{{< docs-imagebox img="/img/docs/v65/loki_derived_fields.png" class="docs-image--no-shadow" caption="Screenshot of the derived fields configuration" >}}
Each derived field consists of:
......
......@@ -71,11 +71,11 @@ frame.add({ time: 1589189388597, content: 'user registered' });
frame.add({ time: 1589189406480, content: 'user logged in' });
```
## Extract parsed fields from your logs
## Extract detected fields from your logs
You can add additional information about each log line by adding more data frame fields.
If a data frame has more than one text field, then Grafana assumes the first field in the data frame to be the actual log line. Any subsequent text fields are treated as [parsed fields]({{< relref "../../explore/index.md#labels-and-parsed-fields" >}}).
If a data frame has more than one text field, then Grafana assumes the first field in the data frame to be the actual log line. Any subsequent text fields are treated as [detected fields]({{< relref "../../explore/index.md#labels-and-detected-fields" >}}).
While you can add any number of custom fields to your data frame, Grafana comes with a couple of dedicated fields: `levels` and `id`. Let's have a closer look at each one.
......
......@@ -210,17 +210,17 @@ Log data can be very repetitive and Explore can help by hiding duplicate log lin
You can change the order of received logs from the default descending order (newest first) to ascending order (oldest first).
### Labels and parsed fields
### Labels and detected fields
Each log row has an extendable area with its labels and parsed fields, for more robust interaction. For all labels we have added the ability to filter for (positive filter) and filter out (negative filter) selected labels. Each field or label also has a stats icon to display ad-hoc statistics in relation to all displayed logs.
Each log row has an extendable area with its labels and detected fields, for more robust interaction. For all labels we have added the ability to filter for (positive filter) and filter out (negative filter) selected labels. Each field or label also has a stats icon to display ad-hoc statistics in relation to all displayed logs.
### Toggle parsed fields
### Toggle detected fields
> **Note:** This feature is only available in Grafana 7.2+.
If your logs are structured in `json` or `logfmt`, then you can show or hide parsed fields. Expand a log line and then click the eye icon to show or hide fields.
If your logs are structured in `json` or `logfmt`, then you can show or hide detected fields. Expand a log line and then click the eye icon to show or hide fields.
{{< docs-imagebox img="/img/docs/explore/parsed-fields-7-2.gif" max-width="800px" caption="Toggling parsed fields in Explore" >}}
{{< docs-imagebox img="/img/docs/explore/parsed-fields-7-2.gif" max-width="800px" caption="Toggling detected fields in Explore" >}}
### Loki-specific features
......
......@@ -12,7 +12,6 @@ list = false
This topic includes the release notes for the Grafana v7.2. For all details, read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
The main highlights are:
- [**New date formatting options added**]({{< relref "#new=date-formatting-options-added" >}})
......@@ -108,7 +107,7 @@ With this awesome contribution from one of our community members, you can now to
{{< docs-imagebox img="/img/docs/v72/explore-toggle-parsed-fields.gif" max-width="800px" caption="Toggling parsed fields in Explore" >}}
The [Toggle parsed fields]({{< relref "../explore/index.md#toggle-parsed-fields" >}}) section has been added to [Explore]({{< relref "../explore/index.md" >}}) as a result of this feature.
The [Toggle parsed fields]({{< relref "../explore/index.md#toggle-detected-fields" >}}) section has been added to [Explore]({{< relref "../explore/index.md" >}}) as a result of this feature.
## Sensitive alert channel settings are now encrypted
......
......@@ -116,7 +116,7 @@ describe('LogsParsers', () => {
expect(parser.test('foo=bar')).toBeTruthy();
});
test('should return parsed fields', () => {
test('should return detected fields', () => {
expect(
parser.getFields(
'foo=bar baz="42 + 1" msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1" time(ms)=50 label{foo}=bar'
......@@ -168,11 +168,11 @@ describe('LogsParsers', () => {
expect(parser.test('{"foo":"bar"}')).toBeTruthy();
});
test('should return parsed fields', () => {
test('should return detected fields', () => {
expect(parser.getFields('{ "foo" : "bar", "baz" : 42 }')).toEqual(['"foo":"bar"', '"baz":42']);
});
test('should return parsed fields for nested quotes', () => {
test('should return detected fields for nested quotes', () => {
expect(parser.getFields(`{"foo":"bar: '[value=\\"42\\"]'"}`)).toEqual([`"foo":"bar: '[value=\\"42\\"]'"`]);
});
......
......@@ -38,7 +38,7 @@ describe('LogDetails', () => {
describe('when labels are present', () => {
it('should render heading', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
expect(wrapper.find({ 'aria-label': 'Log Labels' }).hostNodes()).toHaveLength(1);
expect(wrapper.find({ 'aria-label': 'Log labels' }).hostNodes()).toHaveLength(1);
});
it('should render labels', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
......@@ -56,7 +56,7 @@ describe('LogDetails', () => {
const wrapper = setup(undefined, { entry: 'test=successful' });
expect(wrapper.find({ title: 'Ad-hoc statistics' }).hostNodes()).toHaveLength(1);
});
it('should render parsed fields', () => {
it('should render detected fields', () => {
const wrapper = setup(undefined, { entry: 'test=successful' });
expect(wrapper.text().includes('testsuccessful')).toBe(true);
});
......@@ -64,10 +64,10 @@ describe('LogDetails', () => {
describe('when row entry have parsable fields and labels are present', () => {
it('should render all headings', () => {
const wrapper = setup(undefined, { entry: 'test=successful', labels: { key: 'label' } });
expect(wrapper.find({ 'aria-label': 'Log Labels' })).toHaveLength(1);
expect(wrapper.find({ 'aria-label': 'Parsed Fields' })).toHaveLength(1);
expect(wrapper.find({ 'aria-label': 'Log labels' })).toHaveLength(1);
expect(wrapper.find({ 'aria-label': 'Detected fields' })).toHaveLength(1);
});
it('should render all labels and parsed fields', () => {
it('should render all labels and detected fields', () => {
const wrapper = setup(undefined, {
entry: 'test=successful',
labels: { key: 'label' },
......@@ -84,7 +84,7 @@ describe('LogDetails', () => {
it('should not render headings', () => {
const wrapper = setup(undefined, { entry: '' });
expect(wrapper.find({ 'aria-label': 'Log labels' })).toHaveLength(0);
expect(wrapper.find({ 'aria-label': 'Parsed fields' })).toHaveLength(0);
expect(wrapper.find({ 'aria-label': 'Detected fields' })).toHaveLength(0);
});
});
......
......@@ -22,6 +22,8 @@ import { getAllFields } from './logParser';
//Components
import { LogDetailsRow } from './LogDetailsRow';
import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
export interface Props extends Themeable {
row: LogRowModel;
......@@ -34,9 +36,9 @@ export interface Props extends Themeable {
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
showParsedFields?: string[];
onClickShowParsedField?: (key: string) => void;
onClickHideParsedField?: (key: string) => void;
showDetectedFields?: string[];
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
}
const getStyles = stylesFactory((theme: GrafanaTheme) => {
......@@ -62,7 +64,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
class UnThemedLogDetails extends PureComponent<Props> {
getParser = memoizeOne(getParser);
getStatsForParsedField = (key: string) => {
getStatsForDetectedField = (key: string) => {
const matcher = this.getParser(this.props.row.entry)!.buildMatcher(key);
return calculateFieldStats(this.props.getRows(), matcher);
};
......@@ -79,9 +81,9 @@ class UnThemedLogDetails extends PureComponent<Props> {
className,
onMouseEnter,
onMouseLeave,
onClickShowParsedField,
onClickHideParsedField,
showParsedFields,
onClickShowDetectedField,
onClickHideDetectedField,
showDetectedFields,
getFieldLinks,
} = this.props;
const style = getLogRowStyles(theme, row.logLevel);
......@@ -89,7 +91,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
const labels = row.labels ? row.labels : {};
const labelsAvailable = Object.keys(labels).length > 0;
const fields = getAllFields(row, getFieldLinks);
const parsedFieldsAvailable = fields && fields.length > 0;
const detectedFieldsAvailable = fields && fields.length > 0;
// If logs with error, we are not showing the level color
const levelClassName = cx(!hasError && [style.logsRowLevel, styles.logsRowLevelDetails]);
......@@ -107,8 +109,8 @@ class UnThemedLogDetails extends PureComponent<Props> {
<tbody>
{labelsAvailable && (
<tr>
<td colSpan={5} className={style.logDetailsHeading} aria-label="Log Labels">
Log Labels:
<td colSpan={5} className={style.logDetailsHeading} aria-label="Log labels">
Log labels
</td>
</tr>
)}
......@@ -129,10 +131,19 @@ class UnThemedLogDetails extends PureComponent<Props> {
);
})}
{parsedFieldsAvailable && (
{detectedFieldsAvailable && (
<tr>
<td colSpan={5} className={style.logDetailsHeading} aria-label="Parsed Fields">
Parsed Fields:
<td colSpan={5} className={style.logDetailsHeading} aria-label="Detected fields">
Detected fields
<Tooltip content="Fields that are parsed from log message and detected by Grafana.">
<Icon
name="question-circle"
size="xs"
className={css`
margin-left: 4px;
`}
/>
</Tooltip>
</td>
</tr>
)}
......@@ -144,18 +155,18 @@ class UnThemedLogDetails extends PureComponent<Props> {
parsedKey={key}
parsedValue={value}
links={links}
onClickShowParsedField={onClickShowParsedField}
onClickHideParsedField={onClickHideParsedField}
onClickShowDetectedField={onClickShowDetectedField}
onClickHideDetectedField={onClickHideDetectedField}
getStats={() =>
fieldIndex === undefined
? this.getStatsForParsedField(key)
? this.getStatsForDetectedField(key)
: calculateStats(row.dataFrame.fields[fieldIndex].values.toArray())
}
showParsedFields={showParsedFields}
showDetectedFields={showDetectedFields}
/>
);
})}
{!parsedFieldsAvailable && !labelsAvailable && (
{!detectedFieldsAvailable && !labelsAvailable && (
<tr>
<td colSpan={5} aria-label="No details">
No details available
......
......@@ -20,9 +20,9 @@ export interface Props extends Themeable {
onClickFilterOutLabel?: (key: string, value: string) => void;
links?: Array<LinkModel<Field>>;
getStats: () => LogLabelStatsModel[] | null;
showParsedFields?: string[];
onClickShowParsedField?: (key: string) => void;
onClickHideParsedField?: (key: string) => void;
showDetectedFields?: string[];
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
}
interface State {
......@@ -61,16 +61,16 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
};
showField = () => {
const { onClickShowParsedField, parsedKey } = this.props;
if (onClickShowParsedField) {
onClickShowParsedField(parsedKey);
const { onClickShowDetectedField, parsedKey } = this.props;
if (onClickShowDetectedField) {
onClickShowDetectedField(parsedKey);
}
};
hideField = () => {
const { onClickHideParsedField, parsedKey } = this.props;
if (onClickHideParsedField) {
onClickHideParsedField(parsedKey);
const { onClickHideDetectedField, parsedKey } = this.props;
if (onClickHideDetectedField) {
onClickHideDetectedField(parsedKey);
}
};
......@@ -107,12 +107,12 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
}
render() {
const { theme, parsedKey, parsedValue, isLabel, links, showParsedFields } = this.props;
const { theme, parsedKey, parsedValue, isLabel, links, showDetectedFields } = this.props;
const { showFieldsStats, fieldStats, fieldCount } = this.state;
const styles = getStyles(theme);
const style = getLogRowStyles(theme);
const toggleFieldButton =
!isLabel && showParsedFields && showParsedFields.includes(parsedKey) ? (
!isLabel && showDetectedFields && showDetectedFields.includes(parsedKey) ? (
<IconButton name="eye" className={styles.showingField} title="Hide this field" onClick={this.hideField} />
) : (
<IconButton name="eye" title="Show this field instead of the message" onClick={this.showField} />
......@@ -121,7 +121,7 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
return (
<tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}>
{/* Action buttons - show stats/filter results */}
<td className={style.logsDetailsIcon} colSpan={isLabel ? undefined : 2}>
<td className={style.logsDetailsIcon}>
<IconButton name="signal" title={'Ad-hoc statistics'} onClick={this.showStats} />
</td>
......@@ -138,7 +138,9 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
{!isLabel && (
<>
<td className={style.logsDetailsIcon}>{toggleFieldButton}</td>
<td className={style.logsDetailsIcon} colSpan={2}>
{toggleFieldButton}
</td>
</>
)}
......
......@@ -29,7 +29,7 @@ import { selectThemeVariant } from '../../themes/selectThemeVariant';
//Components
import { LogDetails } from './LogDetails';
import { LogRowMessageParsed } from './LogRowMessageParsed';
import { LogRowMessageDetectedFields } from './LogRowMessageDetectedFields';
import { LogRowMessage } from './LogRowMessage';
import { LogLabels } from './LogLabels';
......@@ -50,9 +50,9 @@ interface Props extends Themeable {
getRowContext: (row: LogRowModel, options?: RowContextOptions) => Promise<DataQueryResponse>;
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
showContextToggle?: (row?: LogRowModel) => boolean;
showParsedFields?: string[];
onClickShowParsedField?: (key: string) => void;
onClickHideParsedField?: (key: string) => void;
showDetectedFields?: string[];
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
}
interface State {
......@@ -149,8 +149,8 @@ class UnThemedLogRow extends PureComponent<Props, State> {
getRows,
onClickFilterLabel,
onClickFilterOutLabel,
onClickShowParsedField,
onClickHideParsedField,
onClickShowDetectedField,
onClickHideDetectedField,
highlighterExpressions,
allowDetails,
row,
......@@ -158,7 +158,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
showContextToggle,
showLabels,
showTime,
showParsedFields,
showDetectedFields,
wrapLogMessage,
theme,
getFieldLinks,
......@@ -198,8 +198,12 @@ class UnThemedLogRow extends PureComponent<Props, State> {
<LogLabels labels={row.uniqueLabels} />
</td>
)}
{showParsedFields && showParsedFields.length > 0 ? (
<LogRowMessageParsed row={row} showParsedFields={showParsedFields!} getFieldLinks={getFieldLinks} />
{showDetectedFields && showDetectedFields.length > 0 ? (
<LogRowMessageDetectedFields
row={row}
showDetectedFields={showDetectedFields!}
getFieldLinks={getFieldLinks}
/>
) : (
<LogRowMessage
highlighterExpressions={highlighterExpressions}
......@@ -225,12 +229,12 @@ class UnThemedLogRow extends PureComponent<Props, State> {
getFieldLinks={getFieldLinks}
onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel}
onClickShowParsedField={onClickShowParsedField}
onClickHideParsedField={onClickHideParsedField}
onClickShowDetectedField={onClickShowDetectedField}
onClickHideDetectedField={onClickHideDetectedField}
getRows={getRows}
row={row}
hasError={hasError}
showParsedFields={showParsedFields}
showDetectedFields={showDetectedFields}
/>
)}
</>
......
......@@ -8,16 +8,16 @@ import { getAllFields } from './logParser';
export interface Props extends Themeable {
row: LogRowModel;
showParsedFields: string[];
showDetectedFields: string[];
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
}
class UnThemedLogRowMessageParsed extends PureComponent<Props> {
class UnThemedLogRowMessageDetectedFields extends PureComponent<Props> {
render() {
const { row, showParsedFields, getFieldLinks } = this.props;
const { row, showDetectedFields, getFieldLinks } = this.props;
const fields = getAllFields(row, getFieldLinks);
const line = showParsedFields
const line = showDetectedFields
.map(parsedKey => {
const field = fields.find(field => {
const { key } = field;
......@@ -37,5 +37,5 @@ class UnThemedLogRowMessageParsed extends PureComponent<Props> {
}
}
export const LogRowMessageParsed = withTheme(UnThemedLogRowMessageParsed);
LogRowMessageParsed.displayName = 'LogRowMessageParsed';
export const LogRowMessageDetectedFields = withTheme(UnThemedLogRowMessageDetectedFields);
LogRowMessageDetectedFields.displayName = 'LogRowMessageDetectedFields';
......@@ -34,9 +34,9 @@ export interface Props extends Themeable {
onClickFilterOutLabel?: (key: string, value: string) => void;
getRowContext?: (row: LogRowModel, options?: RowContextOptions) => Promise<any>;
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
showParsedFields?: string[];
onClickShowParsedField?: (key: string) => void;
onClickHideParsedField?: (key: string) => void;
showDetectedFields?: string[];
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
}
interface State {
......@@ -102,9 +102,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
getFieldLinks,
disableCustomHorizontalScroll,
logsSortOrder,
showParsedFields,
onClickShowParsedField,
onClickHideParsedField,
showDetectedFields,
onClickShowDetectedField,
onClickHideDetectedField,
} = this.props;
const { renderAll } = this.state;
const { logsRowsTable, logsRowsHorizontalScroll } = getLogRowStyles(theme);
......@@ -146,14 +146,14 @@ class UnThemedLogRows extends PureComponent<Props, State> {
showDuplicates={showDuplicates}
showLabels={showLabels}
showTime={showTime}
showParsedFields={showParsedFields}
showDetectedFields={showDetectedFields}
wrapLogMessage={wrapLogMessage}
timeZone={timeZone}
allowDetails={allowDetails}
onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel}
onClickShowParsedField={onClickShowParsedField}
onClickHideParsedField={onClickHideParsedField}
onClickShowDetectedField={onClickShowDetectedField}
onClickHideDetectedField={onClickHideDetectedField}
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
/>
......@@ -170,14 +170,14 @@ class UnThemedLogRows extends PureComponent<Props, State> {
showDuplicates={showDuplicates}
showLabels={showLabels}
showTime={showTime}
showParsedFields={showParsedFields}
showDetectedFields={showDetectedFields}
wrapLogMessage={wrapLogMessage}
timeZone={timeZone}
allowDetails={allowDetails}
onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel}
onClickShowParsedField={onClickShowParsedField}
onClickHideParsedField={onClickHideParsedField}
onClickShowDetectedField={onClickShowDetectedField}
onClickHideDetectedField={onClickHideDetectedField}
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
/>
......
......@@ -49,8 +49,8 @@ const parseMessage = memoizeOne((rowEntry): FieldDef[] => {
return [];
}
// Use parser to highlight detected fields
const parsedFields = parser.getFields(rowEntry);
const fields = parsedFields.map(field => {
const detectedFields = parser.getFields(rowEntry);
const fields = detectedFields.map(field => {
const key = parser.getLabelFromField(field);
const value = parser.getValueFromField(field);
return { key, value };
......
......@@ -79,7 +79,7 @@ interface State {
wrapLogMessage: boolean;
logsSortOrder: LogsSortOrder | null;
isFlipping: boolean;
showParsedFields: string[];
showDetectedFields: string[];
}
export class Logs extends PureComponent<Props, State> {
......@@ -92,7 +92,7 @@ export class Logs extends PureComponent<Props, State> {
wrapLogMessage: store.getBool(SETTINGS_KEYS.wrapLogMessage, true),
logsSortOrder: null,
isFlipping: false,
showParsedFields: [],
showDetectedFields: [],
};
componentWillUnmount() {
......@@ -174,33 +174,33 @@ export class Logs extends PureComponent<Props, State> {
}
};
showParsedField = (key: string) => {
const index = this.state.showParsedFields.indexOf(key);
showDetectedField = (key: string) => {
const index = this.state.showDetectedFields.indexOf(key);
if (index === -1) {
this.setState(state => {
return {
showParsedFields: state.showParsedFields.concat(key),
showDetectedFields: state.showDetectedFields.concat(key),
};
});
}
};
hideParsedField = (key: string) => {
const index = this.state.showParsedFields.indexOf(key);
hideDetectedField = (key: string) => {
const index = this.state.showDetectedFields.indexOf(key);
if (index > -1) {
this.setState(state => {
return {
showParsedFields: state.showParsedFields.filter(k => key !== k),
showDetectedFields: state.showDetectedFields.filter(k => key !== k),
};
});
}
};
clearParsedFields = () => {
clearDetectedFields = () => {
this.setState(state => {
return {
showParsedFields: [],
showDetectedFields: [],
};
});
};
......@@ -230,7 +230,7 @@ export class Logs extends PureComponent<Props, State> {
return null;
}
const { showLabels, showTime, wrapLogMessage, logsSortOrder, isFlipping, showParsedFields } = this.state;
const { showLabels, showTime, wrapLogMessage, logsSortOrder, isFlipping, showDetectedFields } = this.state;
const { dedupStrategy } = this.props;
const hasData = logRows && logRows.length > 0;
const dedupCount = dedupedRows
......@@ -323,18 +323,18 @@ export class Logs extends PureComponent<Props, State> {
/>
)}
{showParsedFields && showParsedFields.length > 0 && (
{showDetectedFields && showDetectedFields.length > 0 && (
<MetaInfoText
metaItems={[
{
label: 'Showing only parsed fields',
value: renderMetaItem(showParsedFields, LogsMetaKind.LabelsMap),
label: 'Showing only detected fields',
value: renderMetaItem(showDetectedFields, LogsMetaKind.LabelsMap),
},
{
label: '',
value: (
<Button variant="secondary" size="sm" onClick={this.clearParsedFields}>
Show all parsed fields
<Button variant="secondary" size="sm" onClick={this.clearDetectedFields}>
Show all detected fields
</Button>
),
},
......@@ -358,9 +358,9 @@ export class Logs extends PureComponent<Props, State> {
timeZone={timeZone}
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
showParsedFields={showParsedFields}
onClickShowParsedField={this.showParsedField}
onClickHideParsedField={this.hideParsedField}
showDetectedFields={showDetectedFields}
onClickShowDetectedField={this.showDetectedField}
onClickHideDetectedField={this.hideDetectedField}
/>
{!loading && !hasData && !scanning && (
......
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