mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore/Loki: Toggle parsed fields in logs view (#26178)
* POC for toggling parsed fields in Explore/Loki * fixed issues * add clear button for toggled parsed fields * fix test-frontend failures * use blue eye instead of eye/eye-slash and update tooltips * break out parseMessage * move indicator to meta data section * clean up LogRowMessageParsed * better label * clean up after making optional
This commit is contained in:
parent
9f2386a219
commit
e962f02fbc
@ -18,16 +18,10 @@ import { getLogRowStyles } from './getLogRowStyles';
|
|||||||
import { stylesFactory } from '../../themes/stylesFactory';
|
import { stylesFactory } from '../../themes/stylesFactory';
|
||||||
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
||||||
|
|
||||||
|
import { parseMessage, FieldDef } from './logParser';
|
||||||
|
|
||||||
//Components
|
//Components
|
||||||
import { LogDetailsRow } from './LogDetailsRow';
|
import { LogDetailsRow } from './LogDetailsRow';
|
||||||
import { MAX_CHARACTERS } from './LogRowMessage';
|
|
||||||
|
|
||||||
type FieldDef = {
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
links?: Array<LinkModel<Field>>;
|
|
||||||
fieldIndex?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface Props extends Themeable {
|
export interface Props extends Themeable {
|
||||||
row: LogRowModel;
|
row: LogRowModel;
|
||||||
@ -39,6 +33,9 @@ export interface Props extends Themeable {
|
|||||||
onClickFilterLabel?: (key: string, value: string) => void;
|
onClickFilterLabel?: (key: string, value: string) => void;
|
||||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||||
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
||||||
|
showParsedFields?: string[];
|
||||||
|
onClickShowParsedField?: (key: string) => void;
|
||||||
|
onClickHideParsedField?: (key: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||||
@ -64,25 +61,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
class UnThemedLogDetails extends PureComponent<Props> {
|
class UnThemedLogDetails extends PureComponent<Props> {
|
||||||
getParser = memoizeOne(getParser);
|
getParser = memoizeOne(getParser);
|
||||||
|
|
||||||
parseMessage = memoizeOne((rowEntry): FieldDef[] => {
|
|
||||||
if (rowEntry.length > MAX_CHARACTERS) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const parser = this.getParser(rowEntry);
|
|
||||||
if (!parser) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
// Use parser to highlight detected fields
|
|
||||||
const parsedFields = parser.getFields(rowEntry);
|
|
||||||
const fields = parsedFields.map(field => {
|
|
||||||
const key = parser.getLabelFromField(field);
|
|
||||||
const value = parser.getValueFromField(field);
|
|
||||||
return { key, value };
|
|
||||||
});
|
|
||||||
|
|
||||||
return fields;
|
|
||||||
});
|
|
||||||
|
|
||||||
getDerivedFields = memoizeOne((row: LogRowModel): FieldDef[] => {
|
getDerivedFields = memoizeOne((row: LogRowModel): FieldDef[] => {
|
||||||
return (
|
return (
|
||||||
row.dataFrame.fields
|
row.dataFrame.fields
|
||||||
@ -117,7 +95,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
|
|||||||
* setup in data source config.
|
* setup in data source config.
|
||||||
*/
|
*/
|
||||||
getAllFields = memoizeOne((row: LogRowModel) => {
|
getAllFields = memoizeOne((row: LogRowModel) => {
|
||||||
const fields = this.parseMessage(row.entry);
|
const fields = parseMessage(row.entry);
|
||||||
const derivedFields = this.getDerivedFields(row);
|
const derivedFields = this.getDerivedFields(row);
|
||||||
const fieldsMap = [...derivedFields, ...fields].reduce((acc, field) => {
|
const fieldsMap = [...derivedFields, ...fields].reduce((acc, field) => {
|
||||||
// Strip enclosing quotes for hashing. When values are parsed from log line the quotes are kept, but if same
|
// Strip enclosing quotes for hashing. When values are parsed from log line the quotes are kept, but if same
|
||||||
@ -154,6 +132,9 @@ class UnThemedLogDetails extends PureComponent<Props> {
|
|||||||
className,
|
className,
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
|
onClickShowParsedField,
|
||||||
|
onClickHideParsedField,
|
||||||
|
showParsedFields,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const style = getLogRowStyles(theme, row.logLevel);
|
const style = getLogRowStyles(theme, row.logLevel);
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
@ -211,11 +192,14 @@ class UnThemedLogDetails extends PureComponent<Props> {
|
|||||||
parsedKey={key}
|
parsedKey={key}
|
||||||
parsedValue={value}
|
parsedValue={value}
|
||||||
links={links}
|
links={links}
|
||||||
|
onClickShowParsedField={onClickShowParsedField}
|
||||||
|
onClickHideParsedField={onClickHideParsedField}
|
||||||
getStats={() =>
|
getStats={() =>
|
||||||
fieldIndex === undefined
|
fieldIndex === undefined
|
||||||
? this.getStatsForParsedField(key)
|
? this.getStatsForParsedField(key)
|
||||||
: calculateStats(row.dataFrame.fields[fieldIndex].values.toArray())
|
: calculateStats(row.dataFrame.fields[fieldIndex].values.toArray())
|
||||||
}
|
}
|
||||||
|
showParsedFields={showParsedFields}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -20,6 +20,9 @@ export interface Props extends Themeable {
|
|||||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||||
links?: Array<LinkModel<Field>>;
|
links?: Array<LinkModel<Field>>;
|
||||||
getStats: () => LogLabelStatsModel[] | null;
|
getStats: () => LogLabelStatsModel[] | null;
|
||||||
|
showParsedFields?: string[];
|
||||||
|
onClickShowParsedField?: (key: string) => void;
|
||||||
|
onClickHideParsedField?: (key: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -44,6 +47,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
label: wordBreakAll;
|
label: wordBreakAll;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
`,
|
`,
|
||||||
|
showingField: css`
|
||||||
|
color: ${theme.palette.blue95};
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -54,6 +60,20 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
|
|||||||
fieldStats: null,
|
fieldStats: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
showField = () => {
|
||||||
|
const { onClickShowParsedField, parsedKey } = this.props;
|
||||||
|
if (onClickShowParsedField) {
|
||||||
|
onClickShowParsedField(parsedKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hideField = () => {
|
||||||
|
const { onClickHideParsedField, parsedKey } = this.props;
|
||||||
|
if (onClickHideParsedField) {
|
||||||
|
onClickHideParsedField(parsedKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
filterLabel = () => {
|
filterLabel = () => {
|
||||||
const { onClickFilterLabel, parsedKey, parsedValue } = this.props;
|
const { onClickFilterLabel, parsedKey, parsedValue } = this.props;
|
||||||
if (onClickFilterLabel) {
|
if (onClickFilterLabel) {
|
||||||
@ -87,14 +107,21 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { theme, parsedKey, parsedValue, isLabel, links } = this.props;
|
const { theme, parsedKey, parsedValue, isLabel, links, showParsedFields } = this.props;
|
||||||
const { showFieldsStats, fieldStats, fieldCount } = this.state;
|
const { showFieldsStats, fieldStats, fieldCount } = this.state;
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
const style = getLogRowStyles(theme);
|
const style = getLogRowStyles(theme);
|
||||||
|
const toggleFieldButton =
|
||||||
|
!isLabel && showParsedFields && showParsedFields.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} />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}>
|
<tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}>
|
||||||
{/* Action buttons - show stats/filter results */}
|
{/* Action buttons - show stats/filter results */}
|
||||||
<td className={style.logsDetailsIcon} colSpan={isLabel ? undefined : 3}>
|
<td className={style.logsDetailsIcon} colSpan={isLabel ? undefined : 2}>
|
||||||
<IconButton name="signal" title={'Ad-hoc statistics'} onClick={this.showStats} />
|
<IconButton name="signal" title={'Ad-hoc statistics'} onClick={this.showStats} />
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -109,6 +136,12 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!isLabel && (
|
||||||
|
<>
|
||||||
|
<td className={style.logsDetailsIcon}>{toggleFieldButton}</td>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Key - value columns */}
|
{/* Key - value columns */}
|
||||||
<td className={style.logDetailsLabel}>{parsedKey}</td>
|
<td className={style.logDetailsLabel}>{parsedKey}</td>
|
||||||
<td className={styles.wordBreakAll}>
|
<td className={styles.wordBreakAll}>
|
||||||
|
@ -27,6 +27,7 @@ import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
|||||||
|
|
||||||
//Components
|
//Components
|
||||||
import { LogDetails } from './LogDetails';
|
import { LogDetails } from './LogDetails';
|
||||||
|
import { LogRowMessageParsed } from './LogRowMessageParsed';
|
||||||
import { LogRowMessage } from './LogRowMessage';
|
import { LogRowMessage } from './LogRowMessage';
|
||||||
import { LogLabels } from './LogLabels';
|
import { LogLabels } from './LogLabels';
|
||||||
|
|
||||||
@ -47,6 +48,9 @@ interface Props extends Themeable {
|
|||||||
getRowContext: (row: LogRowModel, options?: RowContextOptions) => Promise<DataQueryResponse>;
|
getRowContext: (row: LogRowModel, options?: RowContextOptions) => Promise<DataQueryResponse>;
|
||||||
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
||||||
showContextToggle?: (row?: LogRowModel) => boolean;
|
showContextToggle?: (row?: LogRowModel) => boolean;
|
||||||
|
showParsedFields?: string[];
|
||||||
|
onClickShowParsedField?: (key: string) => void;
|
||||||
|
onClickHideParsedField?: (key: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -133,6 +137,8 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
getRows,
|
getRows,
|
||||||
onClickFilterLabel,
|
onClickFilterLabel,
|
||||||
onClickFilterOutLabel,
|
onClickFilterOutLabel,
|
||||||
|
onClickShowParsedField,
|
||||||
|
onClickHideParsedField,
|
||||||
highlighterExpressions,
|
highlighterExpressions,
|
||||||
allowDetails,
|
allowDetails,
|
||||||
row,
|
row,
|
||||||
@ -141,6 +147,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
showContextToggle,
|
showContextToggle,
|
||||||
showLabels,
|
showLabels,
|
||||||
showTime,
|
showTime,
|
||||||
|
showParsedFields,
|
||||||
wrapLogMessage,
|
wrapLogMessage,
|
||||||
theme,
|
theme,
|
||||||
getFieldLinks,
|
getFieldLinks,
|
||||||
@ -175,19 +182,23 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
<LogLabels labels={row.uniqueLabels} />
|
<LogLabels labels={row.uniqueLabels} />
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
<LogRowMessage
|
{showParsedFields && showParsedFields.length > 0 ? (
|
||||||
highlighterExpressions={highlighterExpressions}
|
<LogRowMessageParsed row={row} showParsedFields={showParsedFields!} />
|
||||||
row={row}
|
) : (
|
||||||
getRows={getRows}
|
<LogRowMessage
|
||||||
errors={errors}
|
highlighterExpressions={highlighterExpressions}
|
||||||
hasMoreContextRows={hasMoreContextRows}
|
row={row}
|
||||||
updateLimit={updateLimit}
|
getRows={getRows}
|
||||||
context={context}
|
errors={errors}
|
||||||
contextIsOpen={showContext}
|
hasMoreContextRows={hasMoreContextRows}
|
||||||
showContextToggle={showContextToggle}
|
updateLimit={updateLimit}
|
||||||
wrapLogMessage={wrapLogMessage}
|
context={context}
|
||||||
onToggleContext={this.toggleContext}
|
contextIsOpen={showContext}
|
||||||
/>
|
showContextToggle={showContextToggle}
|
||||||
|
wrapLogMessage={wrapLogMessage}
|
||||||
|
onToggleContext={this.toggleContext}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
{this.state.showDetails && (
|
{this.state.showDetails && (
|
||||||
<LogDetails
|
<LogDetails
|
||||||
@ -198,8 +209,11 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
getFieldLinks={getFieldLinks}
|
getFieldLinks={getFieldLinks}
|
||||||
onClickFilterLabel={onClickFilterLabel}
|
onClickFilterLabel={onClickFilterLabel}
|
||||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||||
|
onClickShowParsedField={onClickShowParsedField}
|
||||||
|
onClickHideParsedField={onClickHideParsedField}
|
||||||
getRows={getRows}
|
getRows={getRows}
|
||||||
row={row}
|
row={row}
|
||||||
|
showParsedFields={showParsedFields}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { LogRowModel } from '@grafana/data';
|
||||||
|
|
||||||
|
import { Themeable } from '../../types/theme';
|
||||||
|
import { withTheme } from '../../themes/index';
|
||||||
|
|
||||||
|
import { parseMessage } from './logParser';
|
||||||
|
|
||||||
|
export interface Props extends Themeable {
|
||||||
|
row: LogRowModel;
|
||||||
|
showParsedFields: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnThemedLogRowMessageParsed extends PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const { row, showParsedFields } = this.props;
|
||||||
|
const fields = parseMessage(row.entry);
|
||||||
|
|
||||||
|
const line = showParsedFields
|
||||||
|
.map(parsedKey => {
|
||||||
|
const field = fields.find(field => {
|
||||||
|
const { key } = field;
|
||||||
|
return key === parsedKey;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
return `${parsedKey}=${field.value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(s => s !== null)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return <td>{line}</td>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LogRowMessageParsed = withTheme(UnThemedLogRowMessageParsed);
|
||||||
|
LogRowMessageParsed.displayName = 'LogRowMessageParsed';
|
@ -34,6 +34,9 @@ export interface Props extends Themeable {
|
|||||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||||
getRowContext?: (row: LogRowModel, options?: RowContextOptions) => Promise<any>;
|
getRowContext?: (row: LogRowModel, options?: RowContextOptions) => Promise<any>;
|
||||||
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
||||||
|
showParsedFields?: string[];
|
||||||
|
onClickShowParsedField?: (key: string) => void;
|
||||||
|
onClickHideParsedField?: (key: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -99,6 +102,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
getFieldLinks,
|
getFieldLinks,
|
||||||
disableCustomHorizontalScroll,
|
disableCustomHorizontalScroll,
|
||||||
logsSortOrder,
|
logsSortOrder,
|
||||||
|
showParsedFields,
|
||||||
|
onClickShowParsedField,
|
||||||
|
onClickHideParsedField,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { renderAll } = this.state;
|
const { renderAll } = this.state;
|
||||||
const { logsRowsTable, logsRowsHorizontalScroll } = getLogRowStyles(theme);
|
const { logsRowsTable, logsRowsHorizontalScroll } = getLogRowStyles(theme);
|
||||||
@ -140,11 +146,14 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
showDuplicates={showDuplicates}
|
showDuplicates={showDuplicates}
|
||||||
showLabels={showLabels}
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
|
showParsedFields={showParsedFields}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
allowDetails={allowDetails}
|
allowDetails={allowDetails}
|
||||||
onClickFilterLabel={onClickFilterLabel}
|
onClickFilterLabel={onClickFilterLabel}
|
||||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||||
|
onClickShowParsedField={onClickShowParsedField}
|
||||||
|
onClickHideParsedField={onClickHideParsedField}
|
||||||
getFieldLinks={getFieldLinks}
|
getFieldLinks={getFieldLinks}
|
||||||
logsSortOrder={logsSortOrder}
|
logsSortOrder={logsSortOrder}
|
||||||
/>
|
/>
|
||||||
@ -161,11 +170,14 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
showDuplicates={showDuplicates}
|
showDuplicates={showDuplicates}
|
||||||
showLabels={showLabels}
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
|
showParsedFields={showParsedFields}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
allowDetails={allowDetails}
|
allowDetails={allowDetails}
|
||||||
onClickFilterLabel={onClickFilterLabel}
|
onClickFilterLabel={onClickFilterLabel}
|
||||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||||
|
onClickShowParsedField={onClickShowParsedField}
|
||||||
|
onClickHideParsedField={onClickHideParsedField}
|
||||||
getFieldLinks={getFieldLinks}
|
getFieldLinks={getFieldLinks}
|
||||||
logsSortOrder={logsSortOrder}
|
logsSortOrder={logsSortOrder}
|
||||||
/>
|
/>
|
||||||
|
32
packages/grafana-ui/src/components/Logs/logParser.ts
Normal file
32
packages/grafana-ui/src/components/Logs/logParser.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Field, getParser, LinkModel } from '@grafana/data';
|
||||||
|
import memoizeOne from 'memoize-one';
|
||||||
|
|
||||||
|
import { MAX_CHARACTERS } from './LogRowMessage';
|
||||||
|
|
||||||
|
const memoizedGetParser = memoizeOne(getParser);
|
||||||
|
|
||||||
|
export type FieldDef = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
links?: Array<LinkModel<Field>>;
|
||||||
|
fieldIndex?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseMessage = memoizeOne((rowEntry): FieldDef[] => {
|
||||||
|
if (rowEntry.length > MAX_CHARACTERS) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const parser = memoizedGetParser(rowEntry);
|
||||||
|
if (!parser) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// Use parser to highlight detected fields
|
||||||
|
const parsedFields = parser.getFields(rowEntry);
|
||||||
|
const fields = parsedFields.map(field => {
|
||||||
|
const key = parser.getLabelFromField(field);
|
||||||
|
const value = parser.getValueFromField(field);
|
||||||
|
return { key, value };
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
});
|
@ -77,18 +77,20 @@ interface State {
|
|||||||
wrapLogMessage: boolean;
|
wrapLogMessage: boolean;
|
||||||
logsSortOrder: LogsSortOrder | null;
|
logsSortOrder: LogsSortOrder | null;
|
||||||
isFlipping: boolean;
|
isFlipping: boolean;
|
||||||
|
showParsedFields: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Logs extends PureComponent<Props, State> {
|
export class Logs extends PureComponent<Props, State> {
|
||||||
flipOrderTimer: NodeJS.Timeout;
|
flipOrderTimer: NodeJS.Timeout;
|
||||||
cancelFlippingTimer: NodeJS.Timeout;
|
cancelFlippingTimer: NodeJS.Timeout;
|
||||||
|
|
||||||
state = {
|
state: State = {
|
||||||
showLabels: store.getBool(SETTINGS_KEYS.showLabels, false),
|
showLabels: store.getBool(SETTINGS_KEYS.showLabels, false),
|
||||||
showTime: store.getBool(SETTINGS_KEYS.showTime, true),
|
showTime: store.getBool(SETTINGS_KEYS.showTime, true),
|
||||||
wrapLogMessage: store.getBool(SETTINGS_KEYS.wrapLogMessage, true),
|
wrapLogMessage: store.getBool(SETTINGS_KEYS.wrapLogMessage, true),
|
||||||
logsSortOrder: null,
|
logsSortOrder: null,
|
||||||
isFlipping: false,
|
isFlipping: false,
|
||||||
|
showParsedFields: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -170,6 +172,37 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
showParsedField = (key: string) => {
|
||||||
|
const index = this.state.showParsedFields.indexOf(key);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
this.setState(state => {
|
||||||
|
return {
|
||||||
|
showParsedFields: state.showParsedFields.concat(key),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hideParsedField = (key: string) => {
|
||||||
|
const index = this.state.showParsedFields.indexOf(key);
|
||||||
|
if (index > -1) {
|
||||||
|
this.setState(state => {
|
||||||
|
return {
|
||||||
|
showParsedFields: state.showParsedFields.filter(k => key !== k),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
clearParsedFields = () => {
|
||||||
|
this.setState(state => {
|
||||||
|
return {
|
||||||
|
showParsedFields: [],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
logRows,
|
logRows,
|
||||||
@ -195,7 +228,7 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { showLabels, showTime, wrapLogMessage, logsSortOrder, isFlipping } = this.state;
|
const { showLabels, showTime, wrapLogMessage, logsSortOrder, isFlipping, showParsedFields } = this.state;
|
||||||
const { dedupStrategy } = this.props;
|
const { dedupStrategy } = this.props;
|
||||||
const hasData = logRows && logRows.length > 0;
|
const hasData = logRows && logRows.length > 0;
|
||||||
const dedupCount = dedupedRows
|
const dedupCount = dedupedRows
|
||||||
@ -290,6 +323,25 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showParsedFields && showParsedFields.length > 0 && (
|
||||||
|
<MetaInfoText
|
||||||
|
metaItems={[
|
||||||
|
{
|
||||||
|
label: 'Showing only parsed fields',
|
||||||
|
value: renderMetaItem(showParsedFields, LogsMetaKind.LabelsMap),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '',
|
||||||
|
value: (
|
||||||
|
<Button variant="secondary" size="sm" onClick={this.clearParsedFields}>
|
||||||
|
Show all parsed fields
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<LogRows
|
<LogRows
|
||||||
logRows={logRows}
|
logRows={logRows}
|
||||||
deduplicatedRows={dedupedRows}
|
deduplicatedRows={dedupedRows}
|
||||||
@ -306,6 +358,9 @@ export class Logs extends PureComponent<Props, State> {
|
|||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
getFieldLinks={getFieldLinks}
|
getFieldLinks={getFieldLinks}
|
||||||
logsSortOrder={logsSortOrder}
|
logsSortOrder={logsSortOrder}
|
||||||
|
showParsedFields={showParsedFields}
|
||||||
|
onClickShowParsedField={this.showParsedField}
|
||||||
|
onClickHideParsedField={this.hideParsedField}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!loading && !hasData && !scanning && (
|
{!loading && !hasData && !scanning && (
|
||||||
|
Loading…
Reference in New Issue
Block a user