Explore: Log message line wrapping options for logs (#20360)

This commit is contained in:
Ivana Huckova 2019-12-03 13:02:44 +01:00 committed by GitHub
parent 2027e1aaee
commit 5a4465a382
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 95 additions and 40 deletions

View File

@ -22,6 +22,7 @@ interface Props extends Themeable {
row: LogRowModel; row: LogRowModel;
showDuplicates: boolean; showDuplicates: boolean;
showTime: boolean; showTime: boolean;
wrapLogMessage: boolean;
timeZone: TimeZone; timeZone: TimeZone;
allowDetails?: boolean; allowDetails?: boolean;
getRows: () => LogRowModel[]; getRows: () => LogRowModel[];
@ -93,6 +94,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
showDuplicates, showDuplicates,
timeZone, timeZone,
showTime, showTime,
wrapLogMessage,
theme, theme,
getFieldLinks, getFieldLinks,
} = this.props; } = this.props;
@ -103,6 +105,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
const showDetailsClassName = showDetails const showDetailsClassName = showDetails
? cx(['fa fa-chevron-down', styles.topVerticalAlign]) ? cx(['fa fa-chevron-down', styles.topVerticalAlign])
: cx(['fa fa-chevron-right', styles.topVerticalAlign]); : cx(['fa fa-chevron-right', styles.topVerticalAlign]);
return ( return (
<div className={style.logsRow}> <div className={style.logsRow}>
{showDuplicates && ( {showDuplicates && (
@ -141,6 +144,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
updateLimit={updateLimit} updateLimit={updateLimit}
context={context} context={context}
showContext={showContext} showContext={showContext}
wrapLogMessage={wrapLogMessage}
onToggleContext={this.toggleContext} onToggleContext={this.toggleContext}
/> />
</div> </div>

View File

@ -21,6 +21,7 @@ interface Props extends Themeable {
row: LogRowModel; row: LogRowModel;
hasMoreContextRows?: HasMoreContextRows; hasMoreContextRows?: HasMoreContextRows;
showContext: boolean; showContext: boolean;
wrapLogMessage: boolean;
errors?: LogRowContextQueryErrors; errors?: LogRowContextQueryErrors;
context?: LogRowContextRows; context?: LogRowContextRows;
highlighterExpressions?: string[]; highlighterExpressions?: string[];
@ -57,6 +58,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
label: whiteSpacePreWrap; label: whiteSpacePreWrap;
white-space: pre-wrap; white-space: pre-wrap;
`, `,
horizontalScroll: css`
label: verticalScroll;
white-space: nowrap;
`,
}; };
}); });
@ -76,6 +81,7 @@ class UnThemedLogRowMessage extends PureComponent<Props, State> {
updateLimit, updateLimit,
context, context,
showContext, showContext,
wrapLogMessage,
onToggleContext, onToggleContext,
} = this.props; } = this.props;
const {} = this.state; const {} = this.state;
@ -91,7 +97,7 @@ class UnThemedLogRowMessage extends PureComponent<Props, State> {
const styles = getStyles(theme); const styles = getStyles(theme);
return ( return (
<div className={style.logsRowMessage}> <div className={style.logsRowMessage}>
<div className={styles.positionRelative}> <div className={cx(styles.positionRelative, { [styles.horizontalScroll]: !wrapLogMessage })}>
{showContext && context && ( {showContext && context && (
<LogRowContext <LogRowContext
row={row} row={row}

View File

@ -14,6 +14,7 @@ describe('LogRows', () => {
dedupStrategy={LogsDedupStrategy.none} dedupStrategy={LogsDedupStrategy.none}
highlighterExpressions={[]} highlighterExpressions={[]}
showTime={false} showTime={false}
wrapLogMessage={true}
timeZone={'utc'} timeZone={'utc'}
/> />
); );
@ -33,6 +34,7 @@ describe('LogRows', () => {
dedupStrategy={LogsDedupStrategy.none} dedupStrategy={LogsDedupStrategy.none}
highlighterExpressions={[]} highlighterExpressions={[]}
showTime={false} showTime={false}
wrapLogMessage={true}
timeZone={'utc'} timeZone={'utc'}
previewLimit={1} previewLimit={1}
/> />
@ -61,6 +63,7 @@ describe('LogRows', () => {
dedupStrategy={LogsDedupStrategy.none} dedupStrategy={LogsDedupStrategy.none}
highlighterExpressions={[]} highlighterExpressions={[]}
showTime={false} showTime={false}
wrapLogMessage={true}
timeZone={'utc'} timeZone={'utc'}
/> />
); );
@ -79,6 +82,7 @@ describe('LogRows', () => {
dedupStrategy={LogsDedupStrategy.none} dedupStrategy={LogsDedupStrategy.none}
highlighterExpressions={[]} highlighterExpressions={[]}
showTime={false} showTime={false}
wrapLogMessage={true}
timeZone={'utc'} timeZone={'utc'}
/> />
); );

View File

@ -18,6 +18,7 @@ export interface Props extends Themeable {
dedupStrategy: LogsDedupStrategy; dedupStrategy: LogsDedupStrategy;
highlighterExpressions?: string[]; highlighterExpressions?: string[];
showTime: boolean; showTime: boolean;
wrapLogMessage: boolean;
timeZone: TimeZone; timeZone: TimeZone;
rowLimit?: number; rowLimit?: number;
allowDetails?: boolean; allowDetails?: boolean;
@ -71,6 +72,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
const { const {
dedupStrategy, dedupStrategy,
showTime, showTime,
wrapLogMessage,
logRows, logRows,
deduplicatedRows, deduplicatedRows,
highlighterExpressions, highlighterExpressions,
@ -84,12 +86,14 @@ class UnThemedLogRows extends PureComponent<Props, State> {
getFieldLinks, getFieldLinks,
} = this.props; } = this.props;
const { renderAll } = this.state; const { renderAll } = this.state;
const { logsRows, logsRowsHorizontalScroll } = getLogRowStyles(theme);
const dedupedRows = deduplicatedRows ? deduplicatedRows : logRows; const dedupedRows = deduplicatedRows ? deduplicatedRows : logRows;
const hasData = logRows && logRows.length > 0; const hasData = logRows && logRows.length > 0;
const dedupCount = dedupedRows const dedupCount = dedupedRows
? dedupedRows.reduce((sum, row) => (row.duplicates ? sum + row.duplicates : sum), 0) ? dedupedRows.reduce((sum, row) => (row.duplicates ? sum + row.duplicates : sum), 0)
: 0; : 0;
const showDuplicates = dedupStrategy !== LogsDedupStrategy.none && dedupCount > 0; const showDuplicates = dedupStrategy !== LogsDedupStrategy.none && dedupCount > 0;
const horizontalScrollWindow = wrapLogMessage ? '' : logsRowsHorizontalScroll;
// Staged rendering // Staged rendering
const processedRows = dedupedRows ? dedupedRows : []; const processedRows = dedupedRows ? dedupedRows : [];
@ -100,45 +104,48 @@ class UnThemedLogRows extends PureComponent<Props, State> {
// React profiler becomes unusable if we pass all rows to all rows and their labels, using getter instead // React profiler becomes unusable if we pass all rows to all rows and their labels, using getter instead
const getRows = this.makeGetRows(processedRows); const getRows = this.makeGetRows(processedRows);
const getRowContext = this.props.getRowContext ? this.props.getRowContext : () => Promise.resolve([]); const getRowContext = this.props.getRowContext ? this.props.getRowContext : () => Promise.resolve([]);
const { logsRows } = getLogRowStyles(theme);
return ( return (
<div className={logsRows}> <div className={logsRows}>
{hasData && <div className={horizontalScrollWindow}>
firstRows.map((row, index) => ( {hasData &&
<LogRow firstRows.map((row, index) => (
key={row.uid} <LogRow
getRows={getRows} key={row.uid}
getRowContext={getRowContext} getRows={getRows}
highlighterExpressions={highlighterExpressions} getRowContext={getRowContext}
row={row} highlighterExpressions={highlighterExpressions}
showDuplicates={showDuplicates} row={row}
showTime={showTime} showDuplicates={showDuplicates}
timeZone={timeZone} showTime={showTime}
allowDetails={allowDetails} wrapLogMessage={wrapLogMessage}
onClickFilterLabel={onClickFilterLabel} timeZone={timeZone}
onClickFilterOutLabel={onClickFilterOutLabel} allowDetails={allowDetails}
getFieldLinks={getFieldLinks} onClickFilterLabel={onClickFilterLabel}
/> onClickFilterOutLabel={onClickFilterOutLabel}
))} getFieldLinks={getFieldLinks}
{hasData && />
renderAll && ))}
lastRows.map((row, index) => ( {hasData &&
<LogRow renderAll &&
key={row.uid} lastRows.map((row, index) => (
getRows={getRows} <LogRow
getRowContext={getRowContext} key={row.uid}
row={row} getRows={getRows}
showDuplicates={showDuplicates} getRowContext={getRowContext}
showTime={showTime} row={row}
timeZone={timeZone} showDuplicates={showDuplicates}
allowDetails={allowDetails} showTime={showTime}
onClickFilterLabel={onClickFilterLabel} wrapLogMessage={wrapLogMessage}
onClickFilterOutLabel={onClickFilterOutLabel} timeZone={timeZone}
getFieldLinks={getFieldLinks} allowDetails={allowDetails}
/> onClickFilterLabel={onClickFilterLabel}
))} onClickFilterOutLabel={onClickFilterOutLabel}
{hasData && !renderAll && <span>Rendering {rowCount - previewLimit!} rows...</span>} getFieldLinks={getFieldLinks}
/>
))}
{hasData && !renderAll && <span>Rendering {rowCount - previewLimit!} rows...</span>}
</div>
</div> </div>
); );
} }

View File

@ -62,6 +62,10 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
table-layout: fixed; table-layout: fixed;
width: 100%; width: 100%;
`, `,
logsRowsHorizontalScroll: css`
label: logs-rows__horizontal-scroll;
overflow-y: scroll;
`,
context: context, context: context,
logsRow: css` logsRow: css`
label: logs-row; label: logs-row;
@ -130,6 +134,7 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
display: table-cell; display: table-cell;
white-space: nowrap; white-space: nowrap;
width: 12.5em; width: 12.5em;
padding-right: 1em;
`, `,
logsRowMessage: css` logsRowMessage: css`
label: logs-row__message; label: logs-row__message;

View File

@ -57,11 +57,13 @@ interface Props {
interface State { interface State {
showTime: boolean; showTime: boolean;
wrapLogMessage: boolean;
} }
export class Logs extends PureComponent<Props, State> { export class Logs extends PureComponent<Props, State> {
state = { state = {
showTime: true, showTime: true,
wrapLogMessage: true,
}; };
onChangeDedup = (dedup: LogsDedupStrategy) => { onChangeDedup = (dedup: LogsDedupStrategy) => {
@ -81,6 +83,15 @@ export class Logs extends PureComponent<Props, State> {
} }
}; };
onChangewrapLogMessage = (event?: React.SyntheticEvent) => {
const target = event && (event.target as HTMLInputElement);
if (target) {
this.setState({
wrapLogMessage: target.checked,
});
}
};
onToggleLogLevel = (hiddenRawLevels: string[]) => { onToggleLogLevel = (hiddenRawLevels: string[]) => {
const hiddenLogLevels: LogLevel[] = hiddenRawLevels.map(level => LogLevel[level as LogLevel]); const hiddenLogLevels: LogLevel[] = hiddenRawLevels.map(level => LogLevel[level as LogLevel]);
this.props.onToggleLogLevel(hiddenLogLevels); this.props.onToggleLogLevel(hiddenLogLevels);
@ -123,7 +134,7 @@ export class Logs extends PureComponent<Props, State> {
return null; return null;
} }
const { showTime } = this.state; const { showTime, wrapLogMessage } = 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
@ -164,6 +175,7 @@ export class Logs extends PureComponent<Props, State> {
<div className="logs-panel-options"> <div className="logs-panel-options">
<div className="logs-panel-controls"> <div className="logs-panel-controls">
<Switch label="Time" checked={showTime} onChange={this.onChangeTime} transparent /> <Switch label="Time" checked={showTime} onChange={this.onChangeTime} transparent />
<Switch label="Wrap lines" checked={wrapLogMessage} onChange={this.onChangewrapLogMessage} transparent />
<ToggleButtonGroup label="Dedup" transparent={true}> <ToggleButtonGroup label="Dedup" transparent={true}>
{Object.keys(LogsDedupStrategy).map((dedupType: string, i) => ( {Object.keys(LogsDedupStrategy).map((dedupType: string, i) => (
<ToggleButton <ToggleButton
@ -202,6 +214,7 @@ export class Logs extends PureComponent<Props, State> {
onClickFilterLabel={onClickFilterLabel} onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel} onClickFilterOutLabel={onClickFilterOutLabel}
showTime={showTime} showTime={showTime}
wrapLogMessage={wrapLogMessage}
timeZone={timeZone} timeZone={timeZone}
getFieldLinks={getFieldLinks} getFieldLinks={getFieldLinks}
/> />

View File

@ -10,7 +10,7 @@ interface LogsPanelProps extends PanelProps<Options> {}
export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({ export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
data, data,
timeZone, timeZone,
options: { showTime, sortOrder }, options: { showTime, wrapLogMessage, sortOrder },
width, width,
}) => { }) => {
if (!data) { if (!data) {
@ -31,6 +31,7 @@ export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
dedupStrategy={LogsDedupStrategy.none} dedupStrategy={LogsDedupStrategy.none}
highlighterExpressions={[]} highlighterExpressions={[]}
showTime={showTime} showTime={showTime}
wrapLogMessage={wrapLogMessage}
timeZone={timeZone} timeZone={timeZone}
allowDetails={true} allowDetails={true}
/> />

View File

@ -20,13 +20,20 @@ export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
onOptionsChange({ ...options, showTime: !showTime }); onOptionsChange({ ...options, showTime: !showTime });
}; };
onTogglewrapLogMessage = () => {
const { options, onOptionsChange } = this.props;
const { wrapLogMessage } = options;
onOptionsChange({ ...options, wrapLogMessage: !wrapLogMessage });
};
onShowValuesChange = (item: SelectableValue<SortOrder>) => { onShowValuesChange = (item: SelectableValue<SortOrder>) => {
const { options, onOptionsChange } = this.props; const { options, onOptionsChange } = this.props;
onOptionsChange({ ...options, sortOrder: item.value }); onOptionsChange({ ...options, sortOrder: item.value });
}; };
render() { render() {
const { showTime, sortOrder } = this.props.options; const { showTime, wrapLogMessage, sortOrder } = this.props.options;
const value = sortOrderOptions.filter(option => option.value === sortOrder)[0]; const value = sortOrderOptions.filter(option => option.value === sortOrder)[0];
return ( return (
@ -34,6 +41,12 @@ export class LogsPanelEditor extends PureComponent<PanelEditorProps<Options>> {
<PanelOptionsGrid> <PanelOptionsGrid>
<PanelOptionsGroup title="Columns"> <PanelOptionsGroup title="Columns">
<Switch label="Time" labelClass="width-10" checked={showTime} onChange={this.onToggleTime} /> <Switch label="Time" labelClass="width-10" checked={showTime} onChange={this.onToggleTime} />
<Switch
label="Wrap lines"
labelClass="width-10"
checked={wrapLogMessage}
onChange={this.onTogglewrapLogMessage}
/>
<div className="gf-form"> <div className="gf-form">
<FormLabel>Order</FormLabel> <FormLabel>Order</FormLabel>
<Select options={sortOrderOptions} value={value} onChange={this.onShowValuesChange} /> <Select options={sortOrderOptions} value={value} onChange={this.onShowValuesChange} />

View File

@ -2,10 +2,12 @@ import { SortOrder } from 'app/core/utils/explore';
export interface Options { export interface Options {
showTime: boolean; showTime: boolean;
wrapLogMessage: boolean;
sortOrder: SortOrder; sortOrder: SortOrder;
} }
export const defaults: Options = { export const defaults: Options = {
showTime: true, showTime: true,
wrapLogMessage: true,
sortOrder: SortOrder.Descending, sortOrder: SortOrder.Descending,
}; };