mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Logs Panel: Limit displayed characters to MAX_CHARACTERS (#96997)
* LogRowMessage: limit displayed characters to MAX_CHARACTERS * LogRowMessage: update ellipsis text * Formatting * Revert test change * LogRowMessage: fix conditional * Extract translations * LogRowMessage: use button for ellipsis * Revert test change * Change fill to outline * Revert test change
This commit is contained in:
parent
d418299780
commit
d69888df2d
@ -5,7 +5,7 @@ import { ComponentProps } from 'react';
|
||||
import { CoreApp, createTheme, LogLevel, LogRowModel } from '@grafana/data';
|
||||
import { IconButton } from '@grafana/ui';
|
||||
|
||||
import { LogRowMessage } from './LogRowMessage';
|
||||
import { LogRowMessage, MAX_CHARACTERS } from './LogRowMessage';
|
||||
import { createLogRow } from './__mocks__/logRow';
|
||||
import { getLogRowStyles } from './getLogRowStyles';
|
||||
|
||||
@ -219,4 +219,19 @@ line3`;
|
||||
expect(onAfter).toHaveBeenCalledWith(expect.anything(), row);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Extremely long log lines', () => {
|
||||
let entry = '';
|
||||
beforeEach(() => {
|
||||
entry = new Array(MAX_CHARACTERS).fill('a').join('') + 'b';
|
||||
});
|
||||
it('Displays an ellipsis for log lines above the character limit', async () => {
|
||||
setup({
|
||||
row: createLogRow({ entry, logLevel: LogLevel.error, timeEpochMs: 1546297200000 }),
|
||||
});
|
||||
expect(screen.getByText(/1 more/)).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByText(/1 more/));
|
||||
expect(screen.queryByText(/1 more/)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { memo, ReactNode, useMemo } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { memo, ReactNode, SyntheticEvent, useMemo, useState } from 'react';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
|
||||
import { CoreApp, findHighlightChunksInText, LogRowContextOptions, LogRowModel } from '@grafana/data';
|
||||
import { CoreApp, findHighlightChunksInText, GrafanaTheme2, LogRowContextOptions, LogRowModel } from '@grafana/data';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { PopoverContent } from '@grafana/ui';
|
||||
import { PopoverContent, useTheme2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
|
||||
import { LogMessageAnsi } from './LogMessageAnsi';
|
||||
import { LogRowMenuCell } from './LogRowMenuCell';
|
||||
@ -44,25 +46,73 @@ interface LogMessageProps {
|
||||
}
|
||||
|
||||
const LogMessage = ({ hasAnsi, entry, highlights, styles }: LogMessageProps) => {
|
||||
const excessCharacters = useMemo(() => entry.length - MAX_CHARACTERS, [entry]);
|
||||
const needsHighlighter =
|
||||
highlights && highlights.length > 0 && highlights[0] && highlights[0].length > 0 && entry.length < MAX_CHARACTERS;
|
||||
highlights && highlights.length > 0 && highlights[0] && highlights[0].length > 0 && excessCharacters <= 0;
|
||||
const searchWords = highlights ?? [];
|
||||
const [showFull, setShowFull] = useState(excessCharacters < 0);
|
||||
const truncatedEntry = useMemo(() => (showFull ? entry : entry.substring(0, MAX_CHARACTERS)), [entry, showFull]);
|
||||
|
||||
if (hasAnsi) {
|
||||
const highlight = needsHighlighter ? { searchWords, highlightClassName: styles.logsRowMatchHighLight } : undefined;
|
||||
return <LogMessageAnsi value={entry} highlight={highlight} />;
|
||||
return <LogMessageAnsi value={truncatedEntry} highlight={highlight} />;
|
||||
} else if (needsHighlighter) {
|
||||
return (
|
||||
<Highlighter
|
||||
textToHighlight={entry}
|
||||
textToHighlight={truncatedEntry}
|
||||
searchWords={searchWords}
|
||||
findChunks={findHighlightChunksInText}
|
||||
highlightClassName={styles.logsRowMatchHighLight}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <>{entry}</>;
|
||||
return (
|
||||
<>
|
||||
{truncatedEntry}
|
||||
{!showFull && <Ellipsis showFull={showFull} toggle={setShowFull} diff={excessCharacters} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface EllipsisProps {
|
||||
showFull: boolean;
|
||||
toggle(state: boolean): void;
|
||||
diff: number;
|
||||
}
|
||||
const Ellipsis = ({ toggle, diff }: EllipsisProps) => {
|
||||
const styles = getEllipsisStyles(useTheme2());
|
||||
const handleClick = (e: SyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
toggle(true);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Trans i18nKey="logs.log-row-message.ellipsis">… </Trans>
|
||||
<span className={styles.showMore} onClick={handleClick}>
|
||||
{diff} <Trans i18nKey="logs.log-row-message.more">more</Trans>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getEllipsisStyles = (theme: GrafanaTheme2) => ({
|
||||
showMore: css({
|
||||
display: 'inline-flex',
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
fontSize: theme.typography.size.sm,
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
height: theme.spacing(3),
|
||||
padding: theme.spacing(0.25, 1),
|
||||
color: theme.colors.secondary.text,
|
||||
border: `1px solid ${theme.colors.border.strong}`,
|
||||
'&:hover': {
|
||||
background: theme.colors.secondary.transparent,
|
||||
borderColor: theme.colors.emphasize(theme.colors.border.strong, 0.25),
|
||||
color: theme.colors.secondary.text,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const restructureLog = (
|
||||
line: string,
|
||||
prettifyLogMessage: boolean,
|
||||
|
@ -1742,6 +1742,10 @@
|
||||
"logs": {
|
||||
"infinite-scroll": {
|
||||
"older-logs": "Older logs"
|
||||
},
|
||||
"log-row-message": {
|
||||
"ellipsis": "… ",
|
||||
"more": "more"
|
||||
}
|
||||
},
|
||||
"migrate-to-cloud": {
|
||||
|
@ -1742,6 +1742,10 @@
|
||||
"logs": {
|
||||
"infinite-scroll": {
|
||||
"older-logs": "Øľđęř ľőģş"
|
||||
},
|
||||
"log-row-message": {
|
||||
"ellipsis": "… ",
|
||||
"more": "mőřę"
|
||||
}
|
||||
},
|
||||
"migrate-to-cloud": {
|
||||
|
Loading…
Reference in New Issue
Block a user