Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore: Refactor deduplication, hiding of logs and Logs component #33531

Merged
merged 8 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
144 changes: 38 additions & 106 deletions public/app/features/explore/Logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
LogLevel,
TimeZone,
AbsoluteTimeRange,
LogsMetaKind,
LogsDedupStrategy,
LogRowModel,
LogsDedupDescription,
Expand All @@ -21,7 +20,6 @@ import {
GrafanaTheme,
} from '@grafana/data';
import {
LogLabels,
RadioButtonGroup,
LogRows,
Button,
Expand All @@ -30,39 +28,23 @@ import {
InlineSwitch,
withTheme,
stylesFactory,
Icon,
Tooltip,
} from '@grafana/ui';
import store from 'app/core/store';
import { dedupLogRows, filterLogLevels } from 'app/core/logs_model';
import { ExploreGraphPanel } from './ExploreGraphPanel';
import { MetaInfoText } from './MetaInfoText';
import { LogsMetaRow } from './LogsMetaRow';
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
import { MAX_CHARACTERS } from '@grafana/ui/src/components/Logs/LogRowMessage';

const SETTINGS_KEYS = {
showLabels: 'grafana.explore.logs.showLabels',
showTime: 'grafana.explore.logs.showTime',
wrapLogMessage: 'grafana.explore.logs.wrapLogMessage',
};

function renderMetaItem(value: any, kind: LogsMetaKind) {
if (kind === LogsMetaKind.LabelsMap) {
return (
<span className="logs-meta-item__labels">
<LogLabels labels={value} />
</span>
);
} else if (kind === LogsMetaKind.Error) {
return <span className="logs-meta-item__error">{value}</span>;
}
return value;
}

interface Props {
logRows: LogRowModel[];
logsMeta?: LogsMetaItem[];
logsSeries?: GraphSeriesXY[];
dedupedRows?: LogRowModel[];
visibleRange?: AbsoluteTimeRange;
width: number;
theme: GrafanaTheme;
Expand All @@ -72,15 +54,12 @@ interface Props {
timeZone: TimeZone;
scanning?: boolean;
scanRange?: RawTimeRange;
dedupStrategy: LogsDedupStrategy;
showContextToggle?: (row?: LogRowModel) => boolean;
onChangeTime: (range: AbsoluteTimeRange) => void;
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
onStartScanning?: () => void;
onStopScanning?: () => void;
onDedupStrategyChange: (dedupStrategy: LogsDedupStrategy) => void;
onToggleLogLevel: (hiddenLogLevels: LogLevel[]) => void;
getRowContext?: (row: LogRowModel, options?: RowContextOptions) => Promise<any>;
getFieldLinks: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
}
Expand All @@ -89,6 +68,8 @@ interface State {
showLabels: boolean;
showTime: boolean;
wrapLogMessage: boolean;
dedupStrategy: LogsDedupStrategy;
hiddenLogLevels: LogLevel[];
logsSortOrder: LogsSortOrder | null;
isFlipping: boolean;
showDetectedFields: string[];
Expand All @@ -103,6 +84,8 @@ export class UnthemedLogs extends PureComponent<Props, State> {
showLabels: store.getBool(SETTINGS_KEYS.showLabels, false),
showTime: store.getBool(SETTINGS_KEYS.showTime, true),
wrapLogMessage: store.getBool(SETTINGS_KEYS.wrapLogMessage, true),
dedupStrategy: LogsDedupStrategy.none,
hiddenLogLevels: [],
logsSortOrder: null,
isFlipping: false,
showDetectedFields: [],
Expand Down Expand Up @@ -134,12 +117,8 @@ export class UnthemedLogs extends PureComponent<Props, State> {
}));
};

onChangeDedup = (dedup: LogsDedupStrategy) => {
const { onDedupStrategyChange } = this.props;
if (this.props.dedupStrategy === dedup) {
return onDedupStrategyChange(LogsDedupStrategy.none);
}
return onDedupStrategyChange(dedup);
onChangeDedup = (dedupStrategy: LogsDedupStrategy) => {
this.setState({ dedupStrategy });
};

onChangeLabels = (event?: React.SyntheticEvent) => {
Expand Down Expand Up @@ -176,8 +155,8 @@ export class UnthemedLogs extends PureComponent<Props, State> {
};

onToggleLogLevel = (hiddenRawLevels: string[]) => {
const hiddenLogLevels: LogLevel[] = hiddenRawLevels.map((level) => LogLevel[level as LogLevel]);
this.props.onToggleLogLevel(hiddenLogLevels);
const hiddenLogLevels = hiddenRawLevels.map((level) => LogLevel[level as LogLevel]);
this.setState({ hiddenLogLevels });
};

onClickScan = (event: React.SyntheticEvent) => {
Expand Down Expand Up @@ -229,6 +208,16 @@ export class UnthemedLogs extends PureComponent<Props, State> {
return !!logRows.some((r) => r.hasUnescapedContent);
});

dedupRows = memoizeOne((logRows: LogRowModel[], dedupStrategy: LogsDedupStrategy) => {
const dedupedRows = dedupLogRows(logRows, dedupStrategy);
const dedupCount = dedupedRows.reduce((sum, row) => (row.duplicates ? sum + row.duplicates : sum), 0);
return { dedupedRows, dedupCount };
});

filterRows = memoizeOne((logRows: LogRowModel[], hiddenLogLevels: LogLevel[]) => {
return filterLogLevels(logRows, new Set(hiddenLogLevels));
});

render() {
const {
logRows,
Expand All @@ -244,55 +233,37 @@ export class UnthemedLogs extends PureComponent<Props, State> {
scanRange,
showContextToggle,
width,
dedupedRows,
absoluteRange,
onChangeTime,
getFieldLinks,
dedupStrategy,
theme,
} = this.props;

const {
showLabels,
showTime,
wrapLogMessage,
dedupStrategy,
hiddenLogLevels,
logsSortOrder,
isFlipping,
showDetectedFields,
forceEscape,
} = this.state;

const styles = getStyles(theme);
const hasData = logRows && logRows.length > 0;
const dedupCount = dedupedRows
? dedupedRows.reduce((sum, row) => (row.duplicates ? sum + row.duplicates : sum), 0)
: 0;
const meta = logsMeta ? [...logsMeta] : [];

if (dedupStrategy !== LogsDedupStrategy.none) {
meta.push({
label: 'Dedup count',
value: dedupCount,
kind: LogsMetaKind.Number,
});
}
const hasUnescapedContent = this.checkUnescapedContent(logRows);

if (logRows.some((r) => r.entry.length > MAX_CHARACTERS)) {
meta.push({
label: 'Info',
value: 'Logs with more than 100,000 characters could not be parsed and highlighted',
kind: LogsMetaKind.String,
});
}
const filteredLogs = this.filterRows(logRows, hiddenLogLevels);
const { dedupedRows, dedupCount } = this.dedupRows(filteredLogs, dedupStrategy);

const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...';
const series = logsSeries ? logsSeries : [];
const styles = getStyles(theme);
const hasUnescapedContent = this.checkUnescapedContent(logRows);

return (
<>
<ExploreGraphPanel
series={series}
series={logsSeries || []}
width={width}
onHiddenSeriesChanged={this.onToggleLogLevel}
loading={loading}
Expand Down Expand Up @@ -340,56 +311,17 @@ export class UnthemedLogs extends PureComponent<Props, State> {
</Button>
</div>

{meta && (
<MetaInfoText
metaItems={meta.map((item) => {
return {
label: item.label,
value: renderMetaItem(item.value, item.kind),
};
})}
/>
)}

{showDetectedFields?.length > 0 && (
<MetaInfoText
metaItems={[
{
label: 'Showing only detected fields',
value: renderMetaItem(showDetectedFields, LogsMetaKind.LabelsMap),
},
{
label: '',
value: (
<Button variant="secondary" size="sm" onClick={this.clearDetectedFields}>
Show all detected fields
</Button>
),
},
]}
/>
)}

{hasUnescapedContent && (
<MetaInfoText
metaItems={[
{
label: 'Your logs might have incorrectly escaped content',
value: (
<Tooltip
content="We suggest to try to fix the escaping of your log lines first. This is an experimental feature, your logs might not be correctly escaped."
placement="right"
>
<Button variant="secondary" size="sm" onClick={this.onEscapeNewlines}>
<span>{forceEscape ? 'Remove escaping' : 'Escape newlines'}&nbsp;</span>
<Icon name="exclamation-triangle" className="muted" size="sm" />
</Button>
</Tooltip>
),
},
]}
/>
)}
<LogsMetaRow
logRows={logRows}
meta={logsMeta || []}
dedupStrategy={dedupStrategy}
dedupCount={dedupCount}
hasUnescapedContent={hasUnescapedContent}
forceEscape={forceEscape}
showDetectedFields={showDetectedFields}
onEscapeNewlines={this.onEscapeNewlines}
clearDetectedFields={this.clearDetectedFields}
/>

<LogRows
logRows={logRows}
Expand Down
27 changes: 1 addition & 26 deletions public/app/features/explore/LogsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { Collapse } from '@grafana/ui';

import { AbsoluteTimeRange, Field, LogLevel, LogRowModel, LogsDedupStrategy, RawTimeRange } from '@grafana/data';
import { AbsoluteTimeRange, Field, LogRowModel, RawTimeRange } from '@grafana/data';

import { ExploreId, ExploreItemState } from 'app/types/explore';
import { StoreState } from 'app/types';

import { splitOpen } from './state/main';
import { updateTimeRange } from './state/time';
import { toggleLogLevelAction, changeDedupStrategy } from './state/explorePane';
import { deduplicatedRowsSelector } from './state/selectors';
import { getTimeZone } from '../profile/state/selectors';
import { LiveLogsWithTheme } from './LiveLogs';
import { Logs } from './Logs';
Expand All @@ -36,18 +34,6 @@ export class LogsContainer extends PureComponent<PropsFromRedux & LogsContainerP
updateTimeRange({ exploreId, absoluteRange });
};

handleDedupStrategyChange = (dedupStrategy: LogsDedupStrategy) => {
this.props.changeDedupStrategy(this.props.exploreId, dedupStrategy);
};

handleToggleLogLevel = (hiddenLogLevels: LogLevel[]) => {
const { exploreId } = this.props;
this.props.toggleLogLevelAction({
exploreId,
hiddenLogLevels,
});
};

getLogRowContext = async (row: LogRowModel, options?: any): Promise<any> => {
const { datasourceInstance } = this.props;

Expand Down Expand Up @@ -80,7 +66,6 @@ export class LogsContainer extends PureComponent<PropsFromRedux & LogsContainerP
logRows,
logsMeta,
logsSeries,
dedupedRows,
onClickFilterLabel,
onClickFilterOutLabel,
onStartScanning,
Expand Down Expand Up @@ -120,20 +105,16 @@ export class LogsContainer extends PureComponent<PropsFromRedux & LogsContainerP
<LogsCrossFadeTransition visible={!isLive}>
<Collapse label="Logs" loading={loading} isOpen>
<Logs
dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
logRows={logRows}
logsMeta={logsMeta}
logsSeries={logsSeries}
dedupedRows={dedupedRows}
highlighterExpressions={logsHighlighterExpressions}
loading={loading}
onChangeTime={this.onChangeTime}
onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel}
onStartScanning={onStartScanning}
onStopScanning={onStopScanning}
onDedupStrategyChange={this.handleDedupStrategyChange}
onToggleLogLevel={this.handleToggleLogLevel}
absoluteRange={absoluteRange}
visibleRange={visibleRange}
timeZone={timeZone}
Expand Down Expand Up @@ -165,9 +146,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
isPaused,
range,
absoluteRange,
dedupStrategy,
} = item;
const dedupedRows = deduplicatedRowsSelector(item) || undefined;
const timeZone = getTimeZone(state.user);

return {
Expand All @@ -179,8 +158,6 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
visibleRange: logsResult?.visibleRange,
scanning,
timeZone,
dedupStrategy,
dedupedRows,
datasourceInstance,
isLive,
isPaused,
Expand All @@ -190,8 +167,6 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
}

const mapDispatchToProps = {
changeDedupStrategy,
toggleLogLevelAction,
updateTimeRange,
splitOpen,
};
Expand Down