mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Log message line wrapping options for logs (#20360)
This commit is contained in:
parent
2027e1aaee
commit
5a4465a382
@ -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>
|
||||||
|
@ -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}
|
||||||
|
@ -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'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
@ -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} />
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user