2023-02-01 15:28:10 +01:00
|
|
|
import { cx } from '@emotion/css';
|
2022-09-19 10:51:46 +02:00
|
|
|
import memoizeOne from 'memoize-one';
|
|
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
|
import Highlighter from 'react-highlight-words';
|
|
|
|
|
|
2023-01-27 15:12:01 +01:00
|
|
|
import {
|
|
|
|
|
LogRowModel,
|
|
|
|
|
findHighlightChunksInText,
|
|
|
|
|
LogsSortOrder,
|
|
|
|
|
CoreApp,
|
|
|
|
|
DataSourceWithLogsContextSupport,
|
|
|
|
|
} from '@grafana/data';
|
2023-02-01 15:28:10 +01:00
|
|
|
import { IconButton, Tooltip } from '@grafana/ui';
|
2022-09-19 10:51:46 +02:00
|
|
|
|
|
|
|
|
import { LogMessageAnsi } from './LogMessageAnsi';
|
|
|
|
|
import { LogRowContext } from './LogRowContext';
|
|
|
|
|
import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider';
|
2023-02-01 15:28:10 +01:00
|
|
|
import { LogRowStyles } from './getLogRowStyles';
|
2022-09-19 10:51:46 +02:00
|
|
|
|
|
|
|
|
export const MAX_CHARACTERS = 100000;
|
|
|
|
|
|
2023-02-01 15:28:10 +01:00
|
|
|
interface Props {
|
2022-09-19 10:51:46 +02:00
|
|
|
row: LogRowModel;
|
|
|
|
|
hasMoreContextRows?: HasMoreContextRows;
|
|
|
|
|
contextIsOpen: boolean;
|
|
|
|
|
wrapLogMessage: boolean;
|
|
|
|
|
prettifyLogMessage: boolean;
|
|
|
|
|
errors?: LogRowContextQueryErrors;
|
|
|
|
|
context?: LogRowContextRows;
|
2022-09-29 10:00:01 +02:00
|
|
|
showRowMenu?: boolean;
|
|
|
|
|
app?: CoreApp;
|
2022-09-29 14:51:20 +02:00
|
|
|
scrollElement?: HTMLDivElement;
|
2022-09-19 10:51:46 +02:00
|
|
|
showContextToggle?: (row?: LogRowModel) => boolean;
|
2023-01-27 15:12:01 +01:00
|
|
|
getLogRowContextUi?: DataSourceWithLogsContextSupport['getLogRowContextUi'];
|
2022-09-19 10:51:46 +02:00
|
|
|
getRows: () => LogRowModel[];
|
2022-10-19 10:01:45 +01:00
|
|
|
onToggleContext: (method: string) => void;
|
2022-09-19 10:51:46 +02:00
|
|
|
updateLimit?: () => void;
|
2023-01-27 15:12:01 +01:00
|
|
|
runContextQuery?: () => void;
|
2022-09-28 13:37:49 +02:00
|
|
|
logsSortOrder?: LogsSortOrder | null;
|
2023-02-01 15:28:10 +01:00
|
|
|
styles: LogRowStyles;
|
2022-09-19 10:51:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderLogMessage(
|
|
|
|
|
hasAnsi: boolean,
|
|
|
|
|
entry: string,
|
|
|
|
|
highlights: string[] | undefined,
|
|
|
|
|
highlightClassName: string
|
|
|
|
|
) {
|
|
|
|
|
const needsHighlighter =
|
|
|
|
|
highlights && highlights.length > 0 && highlights[0] && highlights[0].length > 0 && entry.length < MAX_CHARACTERS;
|
|
|
|
|
const searchWords = highlights ?? [];
|
|
|
|
|
if (hasAnsi) {
|
|
|
|
|
const highlight = needsHighlighter ? { searchWords, highlightClassName } : undefined;
|
|
|
|
|
return <LogMessageAnsi value={entry} highlight={highlight} />;
|
|
|
|
|
} else if (needsHighlighter) {
|
|
|
|
|
return (
|
|
|
|
|
<Highlighter
|
|
|
|
|
textToHighlight={entry}
|
|
|
|
|
searchWords={searchWords}
|
|
|
|
|
findChunks={findHighlightChunksInText}
|
|
|
|
|
highlightClassName={highlightClassName}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
return entry;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const restructureLog = memoizeOne((line: string, prettifyLogMessage: boolean): string => {
|
|
|
|
|
if (prettifyLogMessage) {
|
|
|
|
|
try {
|
|
|
|
|
return JSON.stringify(JSON.parse(line), undefined, 2);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return line;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return line;
|
|
|
|
|
});
|
|
|
|
|
|
2023-02-01 15:28:10 +01:00
|
|
|
export class LogRowMessage extends PureComponent<Props> {
|
2022-09-29 14:51:20 +02:00
|
|
|
logRowRef: React.RefObject<HTMLTableCellElement> = React.createRef();
|
|
|
|
|
|
2022-09-19 10:51:46 +02:00
|
|
|
onContextToggle = (e: React.SyntheticEvent<HTMLElement>) => {
|
|
|
|
|
e.stopPropagation();
|
2022-10-19 10:01:45 +01:00
|
|
|
this.props.onToggleContext('open');
|
2022-09-19 10:51:46 +02:00
|
|
|
};
|
|
|
|
|
|
2022-09-29 14:51:20 +02:00
|
|
|
onShowContextClick = (e: React.SyntheticEvent<HTMLElement, Event>) => {
|
|
|
|
|
const { scrollElement } = this.props;
|
|
|
|
|
this.onContextToggle(e);
|
|
|
|
|
if (scrollElement && this.logRowRef.current) {
|
|
|
|
|
scrollElement.scroll({
|
|
|
|
|
behavior: 'smooth',
|
|
|
|
|
top: scrollElement.scrollTop + this.logRowRef.current.getBoundingClientRect().top - window.innerHeight / 2,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-19 10:51:46 +02:00
|
|
|
render() {
|
|
|
|
|
const {
|
|
|
|
|
row,
|
|
|
|
|
errors,
|
|
|
|
|
hasMoreContextRows,
|
|
|
|
|
updateLimit,
|
2023-01-27 15:12:01 +01:00
|
|
|
runContextQuery,
|
2022-09-19 10:51:46 +02:00
|
|
|
context,
|
|
|
|
|
contextIsOpen,
|
2022-09-29 10:00:01 +02:00
|
|
|
showRowMenu,
|
2022-09-19 10:51:46 +02:00
|
|
|
wrapLogMessage,
|
|
|
|
|
prettifyLogMessage,
|
|
|
|
|
onToggleContext,
|
2022-09-29 10:00:01 +02:00
|
|
|
app,
|
2022-09-28 13:37:49 +02:00
|
|
|
logsSortOrder,
|
2022-09-29 10:00:01 +02:00
|
|
|
showContextToggle,
|
2023-01-27 15:12:01 +01:00
|
|
|
getLogRowContextUi,
|
2023-02-01 15:28:10 +01:00
|
|
|
styles,
|
2022-09-19 10:51:46 +02:00
|
|
|
} = this.props;
|
|
|
|
|
const { hasAnsi, raw } = row;
|
|
|
|
|
const restructuredEntry = restructureLog(raw, prettifyLogMessage);
|
2022-09-29 10:00:01 +02:00
|
|
|
const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false;
|
2023-02-01 15:28:10 +01:00
|
|
|
const inExplore = app === CoreApp.Explore;
|
2022-09-19 10:51:46 +02:00
|
|
|
|
|
|
|
|
return (
|
2022-10-05 16:11:08 +02:00
|
|
|
<>
|
|
|
|
|
{
|
|
|
|
|
// When context is open, the position has to be NOT relative. // Setting the postion as inline-style to
|
2023-02-01 15:28:10 +01:00
|
|
|
// overwrite the more sepecific style definition from `styles.logsRowMessage`.
|
2022-10-05 16:11:08 +02:00
|
|
|
}
|
|
|
|
|
<td
|
|
|
|
|
ref={this.logRowRef}
|
|
|
|
|
style={contextIsOpen ? { position: 'unset' } : undefined}
|
2023-02-01 15:28:10 +01:00
|
|
|
className={styles.logsRowMessage}
|
2022-09-19 10:51:46 +02:00
|
|
|
>
|
2022-10-05 16:11:08 +02:00
|
|
|
<div
|
|
|
|
|
className={cx(
|
|
|
|
|
{ [styles.positionRelative]: wrapLogMessage },
|
|
|
|
|
{ [styles.horizontalScroll]: !wrapLogMessage }
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{contextIsOpen && context && (
|
|
|
|
|
<LogRowContext
|
|
|
|
|
row={row}
|
2023-01-27 15:12:01 +01:00
|
|
|
getLogRowContextUi={getLogRowContextUi}
|
|
|
|
|
runContextQuery={runContextQuery}
|
2022-10-05 16:11:08 +02:00
|
|
|
context={context}
|
|
|
|
|
errors={errors}
|
|
|
|
|
wrapLogMessage={wrapLogMessage}
|
|
|
|
|
hasMoreContextRows={hasMoreContextRows}
|
|
|
|
|
onOutsideClick={onToggleContext}
|
|
|
|
|
logsSortOrder={logsSortOrder}
|
|
|
|
|
onLoadMoreContext={() => {
|
|
|
|
|
if (updateLimit) {
|
|
|
|
|
updateLimit();
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2022-12-21 14:45:47 +01:00
|
|
|
<button className={cx(styles.logLine, styles.positionRelative, { [styles.rowWithContext]: contextIsOpen })}>
|
2023-02-01 15:28:10 +01:00
|
|
|
{renderLogMessage(hasAnsi, restructuredEntry, row.searchWords, styles.logsRowMatchHighLight)}
|
2022-12-21 14:45:47 +01:00
|
|
|
</button>
|
2022-10-05 16:11:08 +02:00
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
{showRowMenu && (
|
2023-02-01 15:28:10 +01:00
|
|
|
<td
|
|
|
|
|
className={cx('log-row-menu-cell', styles.logRowMenuCell, {
|
|
|
|
|
[styles.logRowMenuCellDefaultPosition]: !inExplore,
|
|
|
|
|
[styles.logRowMenuCellExplore]: inExplore && !shouldShowContextToggle,
|
|
|
|
|
[styles.logRowMenuCellExploreWithContextButton]: inExplore && shouldShowContextToggle,
|
|
|
|
|
})}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className={cx('log-row-menu', styles.rowMenu, {
|
|
|
|
|
[styles.rowMenuWithContextButton]: shouldShowContextToggle,
|
|
|
|
|
})}
|
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
|
>
|
2022-09-29 10:00:01 +02:00
|
|
|
{shouldShowContextToggle && (
|
|
|
|
|
<Tooltip placement="top" content={'Show context'}>
|
2022-09-29 14:51:20 +02:00
|
|
|
<IconButton size="md" name="gf-show-context" onClick={this.onShowContextClick} />
|
2022-09-29 10:00:01 +02:00
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
2022-09-19 10:51:46 +02:00
|
|
|
<Tooltip placement="top" content={'Copy'}>
|
2022-10-19 11:58:16 +02:00
|
|
|
<IconButton size="md" name="copy" onClick={() => navigator.clipboard.writeText(restructuredEntry)} />
|
2022-09-19 10:51:46 +02:00
|
|
|
</Tooltip>
|
|
|
|
|
</span>
|
2022-10-05 16:11:08 +02:00
|
|
|
</td>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
2022-09-19 10:51:46 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|