Loki: Visually distinguish error logs for LogQL2 (#28359)

* Loki: Add errored logs and update UI

* Update messaging

* Add icon and tooltip for errored logs

* Update name of variable for more semantic meaning

* Add tests

* Update test

* Refactor, remove unnecessary state

* Update packages/grafana-data/src/types/logs.ts

* Update packages/grafana-ui/src/components/Logs/LogDetails.tsx

Co-authored-by: Giordano Ricci <gio.ricci@grafana.com>

Co-authored-by: Giordano Ricci <gio.ricci@grafana.com>
This commit is contained in:
Ivana Huckova
2020-10-21 19:11:32 +02:00
committed by GitHub
parent 62f5641aa9
commit 3f39b4b601
12 changed files with 162 additions and 11 deletions

View File

@@ -45,6 +45,12 @@ describe('LogDetails', () => {
expect(wrapper.text().includes('key1label1key2label2')).toBe(true);
});
});
describe('when log row has error', () => {
it('should not render log level border', () => {
const wrapper = setup({ hasError: true }, undefined);
expect(wrapper.find({ 'aria-label': 'Log level' }).html()).not.toContain('logs-row__level');
});
});
describe('when row entry has parsable fields', () => {
it('should render heading ', () => {
const wrapper = setup(undefined, { entry: 'test=successful' });

View File

@@ -28,6 +28,7 @@ export interface Props extends Themeable {
showDuplicates: boolean;
getRows: () => LogRowModel[];
className?: string;
hasError?: boolean;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
onClickFilterLabel?: (key: string, value: string) => void;
@@ -70,6 +71,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
const {
row,
theme,
hasError,
onClickFilterOutLabel,
onClickFilterLabel,
getRows,
@@ -88,6 +90,8 @@ class UnThemedLogDetails extends PureComponent<Props> {
const labelsAvailable = Object.keys(labels).length > 0;
const fields = getAllFields(row, getFieldLinks);
const parsedFieldsAvailable = fields && fields.length > 0;
// If logs with error, we are not showing the level color
const levelClassName = cx(!hasError && [style.logsRowLevel, styles.logsRowLevelDetails]);
return (
<tr
@@ -96,7 +100,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
onMouseLeave={onMouseLeave}
>
{showDuplicates && <td />}
<td className={cx(style.logsRowLevel, styles.logsRowLevelDetails)} />
<td className={levelClassName} aria-label="Log level" />
<td colSpan={4}>
<div className={style.logDetailsContainer}>
<table className={style.logDetailsTable}>

View File

@@ -8,8 +8,10 @@ import {
DataQueryResponse,
GrafanaTheme,
dateTimeFormat,
checkLogsError,
} from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { Tooltip } from '../Tooltip/Tooltip';
import { cx, css } from 'emotion';
import {
@@ -72,6 +74,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
label: hoverBackground;
background-color: ${bgColor};
`,
errorLogRow: css`
label: erroredLogRow;
color: ${theme.colors.textWeak};
`,
};
});
/**
@@ -160,22 +166,27 @@ class UnThemedLogRow extends PureComponent<Props, State> {
const { showDetails, showContext, hasHoverBackground } = this.state;
const style = getLogRowStyles(theme, row.logLevel);
const styles = getStyles(theme);
const hoverBackground = cx(style.logsRow, { [styles.hoverBackground]: hasHoverBackground });
const { errorMessage, hasError } = checkLogsError(row);
const logRowBackground = cx(style.logsRow, {
[styles.hoverBackground]: hasHoverBackground,
[styles.errorLogRow]: hasError,
});
return (
<>
<tr
className={hoverBackground}
onMouseEnter={this.addHoverBackground}
onMouseLeave={this.clearHoverBackground}
onClick={this.toggleDetails}
>
<tr className={logRowBackground} onClick={this.toggleDetails}>
{showDuplicates && (
<td className={style.logsRowDuplicates}>
{row.duplicates && row.duplicates > 0 ? `${row.duplicates + 1}x` : null}
</td>
)}
<td className={style.logsRowLevel} />
<td className={cx({ [style.logsRowLevel]: !hasError })}>
{hasError && (
<Tooltip content={`Error: ${errorMessage}`} placement="right" theme="error">
<Icon className={style.logIconError} name="exclamation-triangle" size="sm" />
</Tooltip>
)}
</td>
{!allowDetails && (
<td title={showDetails ? 'Hide log details' : 'See log details'} className={style.logsRowToggleDetails}>
<Icon className={styles.topVerticalAlign} name={showDetails ? 'angle-down' : 'angle-right'} />
@@ -207,7 +218,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
</tr>
{this.state.showDetails && (
<LogDetails
className={hoverBackground}
className={logRowBackground}
onMouseEnter={this.addHoverBackground}
onMouseLeave={this.clearHoverBackground}
showDuplicates={showDuplicates}
@@ -218,6 +229,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
onClickHideParsedField={onClickHideParsedField}
getRows={getRows}
row={row}
hasError={hasError}
showParsedFields={showParsedFields}
/>
)}

View File

@@ -9,6 +9,7 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
let logColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.gray2 }, theme.type);
const borderColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.gray2 }, theme.type);
const bgColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.dark4 }, theme.type);
const hoverBgColor = selectThemeVariant({ light: theme.palette.gray7, dark: theme.palette.dark2 }, theme.type);
const context = css`
label: context;
visibility: hidden;
@@ -92,7 +93,7 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
}
&:hover {
background: ${theme.colors.bodyBg};
background: ${hoverBgColor};
}
`,
logsRowDuplicates: css`
@@ -116,6 +117,10 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
background-color: ${logColor};
}
`,
logIconError: css`
color: ${theme.palette.red};
margin-left: -5px;
`,
logsRowToggleDetails: css`
label: logs-row-toggle-details__level;
position: relative;