Files
grafana/public/app/features/logs/components/LogRowMessage.tsx
Sven Grossmann 68637059c4 Logs: Add permalink to log lines (#69464)
* create explore panel state for logs

* add props to LogRows and unify

* pass properties from explore to logs

* add css

* implement button and scrolling

* export and use `getUrlStateFromPaneState`

* make `scrollIntoView` optional

* change state handling for permalinks

* change link icon

* removed unused state

* add tests for `LogRowMessage`

* remove unused prop

* fix name

* reorg component

* add `LogRow` tests

* add test for `Logs`

* Update public/app/features/logs/components/LogRow.test.tsx

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* Update public/app/features/explore/Logs/Logs.test.tsx

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* improve types in test

* fix props export in Logs.tsx

* fix props export in LogRowMessage.tsx

* fix props export in LogRow.tsx

* fixed import

* fix theme import

* remove hidden style

* add better test names

* change to `log line` rather logline

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* fix tooltips

* remove unused css

---------

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
2023-06-16 14:07:51 +02:00

141 lines
4.4 KiB
TypeScript

import { cx } from '@emotion/css';
import memoizeOne from 'memoize-one';
import React, { PureComponent } from 'react';
import Highlighter from 'react-highlight-words';
import { CoreApp, findHighlightChunksInText, LogRowModel } from '@grafana/data';
import { ClipboardButton, IconButton } from '@grafana/ui';
import { LogMessageAnsi } from './LogMessageAnsi';
import { LogRowStyles } from './getLogRowStyles';
export const MAX_CHARACTERS = 100000;
interface Props {
row: LogRowModel;
wrapLogMessage: boolean;
prettifyLogMessage: boolean;
app?: CoreApp;
showContextToggle?: (row?: LogRowModel) => boolean;
onOpenContext: (row: LogRowModel) => void;
onPermalinkClick?: (row: LogRowModel) => Promise<void>;
styles: LogRowStyles;
}
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;
});
export class LogRowMessage extends PureComponent<Props> {
onShowContextClick = (e: React.SyntheticEvent<HTMLElement, Event>) => {
const { onOpenContext } = this.props;
e.stopPropagation();
onOpenContext(this.props.row);
};
onLogRowClick = (e: React.SyntheticEvent) => {
e.stopPropagation();
};
getLogText = () => {
const { row, prettifyLogMessage } = this.props;
const { raw } = row;
return restructureLog(raw, prettifyLogMessage);
};
render() {
const { row, wrapLogMessage, prettifyLogMessage, showContextToggle, styles, onPermalinkClick } = this.props;
const { hasAnsi, raw } = row;
const restructuredEntry = restructureLog(raw, prettifyLogMessage);
const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false;
return (
<>
{
// When context is open, the position has to be NOT relative. // Setting the postion as inline-style to
// overwrite the more sepecific style definition from `styles.logsRowMessage`.
}
<td className={styles.logsRowMessage}>
<div
className={cx(
{ [styles.positionRelative]: wrapLogMessage },
{ [styles.horizontalScroll]: !wrapLogMessage }
)}
>
<button className={cx(styles.logLine, styles.positionRelative)}>
{renderLogMessage(hasAnsi, restructuredEntry, row.searchWords, styles.logsRowMatchHighLight)}
</button>
</div>
</td>
<td className={cx('log-row-menu-cell', styles.logRowMenuCell)}>
<span className={cx('log-row-menu', styles.rowMenu)} onClick={this.onLogRowClick}>
{shouldShowContextToggle && (
<IconButton
size="md"
name="gf-show-context"
onClick={this.onShowContextClick}
tooltip="Show context"
tooltipPlacement="top"
aria-label="Show context"
/>
)}
<ClipboardButton
className={styles.copyLogButton}
icon="copy"
variant="secondary"
fill="text"
size="md"
getText={this.getLogText}
tooltip="Copy to clipboard"
tooltipPlacement="top"
/>
{onPermalinkClick && row.uid && (
<IconButton
tooltip="Copy shortlink"
aria-label="Copy shortlink"
tooltipPlacement="top"
size="md"
name="share-alt"
onClick={() => onPermalinkClick(row)}
/>
)}
</span>
</td>
</>
);
}
}