Logs: Show copy button independently from context (#55934)

This commit is contained in:
Sven Grossmann 2022-09-29 10:00:01 +02:00 committed by GitHub
parent bf07deb992
commit 998a368c69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 31 deletions

View File

@ -22,6 +22,7 @@ import {
LoadingState,
SplitOpen,
DataQueryResponse,
CoreApp,
} from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import {
@ -475,6 +476,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
showDetectedFields={showDetectedFields}
onClickShowDetectedField={this.showDetectedField}
onClickHideDetectedField={this.hideDetectedField}
app={CoreApp.Explore}
/>
</div>
<LogsNavigation

View File

@ -12,6 +12,7 @@ import {
checkLogsError,
escapeUnescapedString,
GrafanaTheme2,
CoreApp,
} from '@grafana/data';
import { styleMixins, withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui';
@ -42,6 +43,8 @@ interface Props extends Themeable2 {
logsSortOrder?: LogsSortOrder | null;
forceEscape?: boolean;
showDetectedFields?: string[];
showRowMenu?: boolean;
app?: CoreApp;
getRows: () => LogRowModel[];
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
@ -52,6 +55,7 @@ interface Props extends Themeable2 {
onClickShowDetectedField?: (key: string) => void;
onClickHideDetectedField?: (key: string) => void;
onLogRowHover?: (row?: LogRowModel) => void;
toggleContextIsOpen?: () => void;
}
interface State {
@ -91,6 +95,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
};
toggleContext = () => {
this.props.toggleContextIsOpen?.();
this.setState((state) => {
return {
showContext: !state.showContext,
@ -132,6 +137,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
row,
showDuplicates,
showContextToggle,
showRowMenu,
showLabels,
showTime,
showDetectedFields,
@ -141,6 +147,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
getFieldLinks,
forceEscape,
onLogRowHover,
app,
} = this.props;
const { showDetails, showContext } = this.state;
const style = getLogRowStyles(theme, row.logLevel);
@ -207,9 +214,11 @@ class UnThemedLogRow extends PureComponent<Props, State> {
context={context}
contextIsOpen={showContext}
showContextToggle={showContextToggle}
showRowMenu={showRowMenu}
wrapLogMessage={wrapLogMessage}
prettifyLogMessage={prettifyLogMessage}
onToggleContext={this.toggleContext}
app={app}
logsSortOrder={logsSortOrder}
/>
)}

View File

@ -4,7 +4,7 @@ import React, { PureComponent } from 'react';
import Highlighter from 'react-highlight-words';
import tinycolor from 'tinycolor2';
import { LogRowModel, findHighlightChunksInText, GrafanaTheme2, LogsSortOrder } from '@grafana/data';
import { LogRowModel, findHighlightChunksInText, GrafanaTheme2, LogsSortOrder, CoreApp } from '@grafana/data';
import { withTheme2, Themeable2, IconButton, Tooltip } from '@grafana/ui';
import { LogMessageAnsi } from './LogMessageAnsi';
@ -12,8 +12,6 @@ import { LogRowContext } from './LogRowContext';
import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider';
import { getLogRowStyles } from './getLogRowStyles';
//Components
export const MAX_CHARACTERS = 100000;
interface Props extends Themeable2 {
@ -24,6 +22,8 @@ interface Props extends Themeable2 {
prettifyLogMessage: boolean;
errors?: LogRowContextQueryErrors;
context?: LogRowContextRows;
showRowMenu?: boolean;
app?: CoreApp;
showContextToggle?: (row?: LogRowModel) => boolean;
getRows: () => LogRowModel[];
onToggleContext: () => void;
@ -31,7 +31,7 @@ interface Props extends Themeable2 {
logsSortOrder?: LogsSortOrder | null;
}
const getStyles = (theme: GrafanaTheme2) => {
const getStyles = (theme: GrafanaTheme2, showContextButton: boolean, isInDashboard: boolean | undefined) => {
const outlineColor = tinycolor(theme.components.dashboard.background).setAlpha(0.7).toRgbString();
return {
@ -52,7 +52,7 @@ const getStyles = (theme: GrafanaTheme2) => {
display: block;
margin-left: 0px;
`,
contextButton: css`
rowMenu: css`
display: flex;
flex-wrap: nowrap;
flex-direction: row;
@ -60,15 +60,16 @@ const getStyles = (theme: GrafanaTheme2) => {
justify-content: space-evenly;
align-items: center;
position: absolute;
right: -8px;
right: ${isInDashboard ? '0px' : '-8px'};
top: 0;
bottom: auto;
width: 80px;
height: 36px;
background: ${theme.colors.background.primary};
box-shadow: ${theme.shadows.z3};
padding: ${theme.spacing(0, 0, 0, 0.5)};
z-index: 100;
visibility: hidden;
width: ${showContextButton ? '80px' : '40px'};
`,
};
};
@ -125,17 +126,20 @@ class UnThemedLogRowMessage extends PureComponent<Props> {
updateLimit,
context,
contextIsOpen,
showContextToggle,
showRowMenu,
wrapLogMessage,
prettifyLogMessage,
onToggleContext,
app,
logsSortOrder,
showContextToggle,
} = this.props;
const style = getLogRowStyles(theme, row.logLevel);
const { hasAnsi, raw } = row;
const restructuredEntry = restructureLog(raw, prettifyLogMessage);
const styles = getStyles(theme);
const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false;
const styles = getStyles(theme, shouldShowContextToggle, app === CoreApp.Dashboard);
return (
// When context is open, the position has to be NOT relative.
@ -163,14 +167,13 @@ class UnThemedLogRowMessage extends PureComponent<Props> {
<span className={cx(styles.positionRelative, { [styles.rowWithContext]: contextIsOpen })}>
{renderLogMessage(hasAnsi, restructuredEntry, row.searchWords, style.logsRowMatchHighLight)}
</span>
{!contextIsOpen && showContextToggle?.(row) && (
<span
className={cx('log-row-context', style.context, styles.contextButton)}
onClick={(e) => e.stopPropagation()}
>
<Tooltip placement="top" content={'Show context'}>
<IconButton size="md" name="gf-show-context" onClick={this.onContextToggle} />
</Tooltip>
{showRowMenu && (
<span className={cx('log-row-menu', styles.rowMenu)} onClick={(e) => e.stopPropagation()}>
{shouldShowContextToggle && (
<Tooltip placement="top" content={'Show context'}>
<IconButton size="md" name="gf-show-context" onClick={this.onContextToggle} />
</Tooltip>
)}
<Tooltip placement="top" content={'Copy'}>
<IconButton
size="md"

View File

@ -1,7 +1,16 @@
import memoizeOne from 'memoize-one';
import React, { PureComponent } from 'react';
import { TimeZone, LogsDedupStrategy, LogRowModel, Field, LinkModel, LogsSortOrder, sortLogRows } from '@grafana/data';
import {
TimeZone,
LogsDedupStrategy,
LogRowModel,
Field,
LinkModel,
LogsSortOrder,
sortLogRows,
CoreApp,
} from '@grafana/data';
import { withTheme2, Themeable2 } from '@grafana/ui';
//Components
@ -25,6 +34,7 @@ export interface Props extends Themeable2 {
previewLimit?: number;
forceEscape?: boolean;
showDetectedFields?: string[];
app?: CoreApp;
showContextToggle?: (row?: LogRowModel) => boolean;
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
@ -37,6 +47,7 @@ export interface Props extends Themeable2 {
interface State {
renderAll: boolean;
contextIsOpen: boolean;
}
class UnThemedLogRows extends PureComponent<Props, State> {
@ -48,6 +59,18 @@ class UnThemedLogRows extends PureComponent<Props, State> {
state: State = {
renderAll: false,
contextIsOpen: false,
};
/**
* Toggle the `contextIsOpen` state when a context of one LogRow is opened in order to not show the menu of the other log rows.
*/
toggleContextIsOpen = (): void => {
this.setState((state) => {
return {
contextIsOpen: !state.contextIsOpen,
};
});
};
componentDidMount() {
@ -100,8 +123,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
onClickHideDetectedField,
forceEscape,
onLogRowHover,
app,
} = this.props;
const { renderAll } = this.state;
const { renderAll, contextIsOpen } = this.state;
const { logsRowsTable } = getLogRowStyles(theme);
const dedupedRows = deduplicatedRows ? deduplicatedRows : logRows;
const hasData = logRows && logRows.length > 0;
@ -130,6 +154,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
getRowContext={getRowContext}
row={row}
showContextToggle={showContextToggle}
showRowMenu={!contextIsOpen}
showDuplicates={showDuplicates}
showLabels={showLabels}
showTime={showTime}
@ -145,7 +170,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
forceEscape={forceEscape}
toggleContextIsOpen={this.toggleContextIsOpen}
onLogRowHover={onLogRowHover}
app={app}
/>
))}
{hasData &&
@ -157,6 +184,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
getRowContext={getRowContext}
row={row}
showContextToggle={showContextToggle}
showRowMenu={!contextIsOpen}
showDuplicates={showDuplicates}
showLabels={showLabels}
showTime={showTime}
@ -172,7 +200,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
getFieldLinks={getFieldLinks}
logsSortOrder={logsSortOrder}
forceEscape={forceEscape}
toggleContextIsOpen={this.toggleContextIsOpen}
onLogRowHover={onLogRowHover}
app={app}
/>
))}
{hasData && !renderAll && (

View File

@ -45,13 +45,6 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => {
font-size: ${theme.typography.bodySmall.fontSize};
width: 100%;
`,
context: css`
label: context;
visibility: hidden;
white-space: nowrap;
position: relative;
margin-left: 10px;
`,
logsRow: css`
label: logs-row;
width: 100%;
@ -59,13 +52,9 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => {
vertical-align: top;
&:hover {
.log-row-context {
.log-row-menu {
visibility: visible;
z-index: 1;
text-decoration: underline;
&:hover {
color: ${theme.colors.warning.main};
}
}
}

View File

@ -10,6 +10,7 @@ import {
LogRowModel,
DataHoverClearEvent,
DataHoverEvent,
CoreApp,
} from '@grafana/data';
import { CustomScrollbar, useStyles2, usePanelContext } from '@grafana/ui';
import { dataFrameToLogsModel, dedupLogRows, COMMON_LABELS } from 'app/core/logsModel';
@ -116,6 +117,7 @@ export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
enableLogDetails={enableLogDetails}
previewLimit={isAscending ? logRows.length : undefined}
onLogRowHover={onLogRowHover}
app={CoreApp.Dashboard}
/>
{showCommonLabels && isAscending && renderCommonLabels()}
</div>