Explore/Logs: Escaping of incorrectly escaped log lines (#31352)

* POC: Escaping of incorrectly escaped log lines

* Remove unused import

* Fix test, change copy

* Make escapedNewlines optional

* Fix typechecks

* Remove loading state from the escaping button

* Update namings
This commit is contained in:
Ivana Huckova
2021-03-03 18:32:27 +01:00
committed by GitHub
parent 43d4a593ae
commit 4c2e5fcbd0
13 changed files with 83 additions and 17 deletions

View File

@@ -62,6 +62,7 @@ export interface LogRowModel {
// Actual log line
entry: string;
hasAnsi: boolean;
hasUnescapedContent: boolean;
labels: Labels;
logLevel: LogLevel;
raw: string;

View File

@@ -295,6 +295,7 @@ describe('sortLogsResult', () => {
dataFrame: new MutableDataFrame(),
entry: '',
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
logLevel: LogLevel.info,
raw: '',
@@ -312,6 +313,7 @@ describe('sortLogsResult', () => {
dataFrame: new MutableDataFrame(),
entry: '',
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
logLevel: LogLevel.info,
raw: '',

View File

@@ -223,3 +223,6 @@ export const checkLogsError = (logRow: LogRowModel): { hasError: boolean; errorM
hasError: false,
};
};
export const escapeUnescapedString = (string: string) =>
string.replace(/\\n|\\t|\\r/g, (match: string) => (match.slice(1) === 't' ? '\t' : '\n'));

View File

@@ -20,6 +20,7 @@ const setup = (propOverrides?: Partial<Props>, rowOverrides?: Partial<LogRowMode
timeLocal: '',
timeUtc: '',
hasAnsi: false,
hasUnescapedContent: false,
entry: '',
raw: '',
uid: '0',

View File

@@ -9,6 +9,7 @@ import {
GrafanaTheme,
dateTimeFormat,
checkLogsError,
escapeUnescapedString,
} from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { Tooltip } from '../Tooltip/Tooltip';
@@ -42,6 +43,8 @@ interface Props extends Themeable {
timeZone: TimeZone;
allowDetails?: boolean;
logsSortOrder?: LogsSortOrder | null;
forceEscape?: boolean;
showDetectedFields?: string[];
getRows: () => LogRowModel[];
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
@@ -49,7 +52,6 @@ interface Props extends Themeable {
getRowContext: (row: LogRowModel, options?: RowContextOptions) => Promise<DataQueryResponse>;
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
showContextToggle?: (row?: LogRowModel) => boolean;
showDetectedFields?: string[];
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
}
@@ -139,6 +141,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
wrapLogMessage,
theme,
getFieldLinks,
forceEscape,
} = this.props;
const { showDetails, showContext } = this.state;
const style = getLogRowStyles(theme, row.logLevel);
@@ -148,12 +151,15 @@ class UnThemedLogRow extends PureComponent<Props, State> {
[styles.errorLogRow]: hasError,
});
const processedRow =
row.hasUnescapedContent && forceEscape ? { ...row, entry: escapeUnescapedString(row.entry) } : row;
return (
<>
<tr className={logRowBackground} onClick={this.toggleDetails}>
{showDuplicates && (
<td className={style.logsRowDuplicates}>
{row.duplicates && row.duplicates > 0 ? `${row.duplicates + 1}x` : null}
{processedRow.duplicates && processedRow.duplicates > 0 ? `${processedRow.duplicates + 1}x` : null}
</td>
)}
<td className={cx({ [style.logsRowLevel]: !hasError })}>
@@ -169,14 +175,14 @@ class UnThemedLogRow extends PureComponent<Props, State> {
</td>
)}
{showTime && <td className={style.logsRowLocalTime}>{this.renderTimeStamp(row.timeEpochMs)}</td>}
{showLabels && row.uniqueLabels && (
{showLabels && processedRow.uniqueLabels && (
<td className={style.logsRowLabels}>
<LogLabels labels={row.uniqueLabels} />
<LogLabels labels={processedRow.uniqueLabels} />
</td>
)}
{showDetectedFields && showDetectedFields.length > 0 ? (
<LogRowMessageDetectedFields
row={row}
row={processedRow}
showDetectedFields={showDetectedFields!}
getFieldLinks={getFieldLinks}
wrapLogMessage={wrapLogMessage}
@@ -184,7 +190,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
) : (
<LogRowMessage
highlighterExpressions={highlighterExpressions}
row={row}
row={processedRow}
getRows={getRows}
errors={errors}
hasMoreContextRows={hasMoreContextRows}
@@ -207,7 +213,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
onClickShowDetectedField={onClickShowDetectedField}
onClickHideDetectedField={onClickHideDetectedField}
getRows={getRows}
row={row}
row={processedRow}
wrapLogMessage={wrapLogMessage}
hasError={hasError}
showDetectedFields={showDetectedFields}
@@ -219,16 +225,12 @@ class UnThemedLogRow extends PureComponent<Props, State> {
render() {
const { showContext } = this.state;
const { logsSortOrder } = this.props;
const { logsSortOrder, row, getRowContext } = this.props;
if (showContext) {
return (
<>
<LogRowContextProvider
row={this.props.row}
getRowContext={this.props.getRowContext}
logsSortOrder={logsSortOrder}
>
<LogRowContextProvider row={row} getRowContext={getRowContext} logsSortOrder={logsSortOrder}>
{({ result, errors, hasMoreContextRows, updateLimit }) => {
return <>{this.renderLogRow(result, errors, hasMoreContextRows, updateLimit)}</>;
}}

View File

@@ -181,6 +181,7 @@ const row: LogRowModel = {
entry: '4',
labels: (null as any) as Labels,
hasAnsi: false,
hasUnescapedContent: false,
raw: '4',
logLevel: LogLevel.info,
timeEpochMs: 4,

View File

@@ -155,6 +155,7 @@ const makeLog = (overrides: Partial<LogRowModel>): LogRowModel => {
logLevel: LogLevel.debug,
entry,
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
raw: entry,
timeFromNow: '',

View File

@@ -17,7 +17,6 @@ export interface Props extends Themeable {
deduplicatedRows?: LogRowModel[];
dedupStrategy: LogsDedupStrategy;
highlighterExpressions?: string[];
showContextToggle?: (row?: LogRowModel) => boolean;
showLabels: boolean;
showTime: boolean;
wrapLogMessage: boolean;
@@ -28,11 +27,13 @@ export interface Props extends Themeable {
// Passed to fix problems with inactive scrolling in Logs Panel
// Can be removed when we unify scrolling for Panel and Explore
disableCustomHorizontalScroll?: boolean;
forceEscape?: boolean;
showDetectedFields?: string[];
showContextToggle?: (row?: LogRowModel) => boolean;
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
getRowContext?: (row: LogRowModel, options?: RowContextOptions) => Promise<any>;
getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
showDetectedFields?: string[];
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
}
@@ -101,6 +102,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
showDetectedFields,
onClickShowDetectedField,
onClickHideDetectedField,
forceEscape,
} = this.props;
const { renderAll } = this.state;
const { logsRowsTable, logsRowsHorizontalScroll } = getLogRowStyles(theme);
@@ -151,6 +153,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
onClickHideDetectedField={onClickHideDetectedField}
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
forceEscape={forceEscape}
/>
))}
{hasData &&
@@ -175,6 +178,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
onClickHideDetectedField={onClickHideDetectedField}
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
forceEscape={forceEscape}
/>
))}
{hasData && !renderAll && (