mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Logs: Show copy button independently from context (#55934)
This commit is contained in:
parent
bf07deb992
commit
998a368c69
@ -22,6 +22,7 @@ import {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
SplitOpen,
|
SplitOpen,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
|
CoreApp,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
@ -475,6 +476,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
showDetectedFields={showDetectedFields}
|
showDetectedFields={showDetectedFields}
|
||||||
onClickShowDetectedField={this.showDetectedField}
|
onClickShowDetectedField={this.showDetectedField}
|
||||||
onClickHideDetectedField={this.hideDetectedField}
|
onClickHideDetectedField={this.hideDetectedField}
|
||||||
|
app={CoreApp.Explore}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<LogsNavigation
|
<LogsNavigation
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
checkLogsError,
|
checkLogsError,
|
||||||
escapeUnescapedString,
|
escapeUnescapedString,
|
||||||
GrafanaTheme2,
|
GrafanaTheme2,
|
||||||
|
CoreApp,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { styleMixins, withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui';
|
import { styleMixins, withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui';
|
||||||
|
|
||||||
@ -42,6 +43,8 @@ interface Props extends Themeable2 {
|
|||||||
logsSortOrder?: LogsSortOrder | null;
|
logsSortOrder?: LogsSortOrder | null;
|
||||||
forceEscape?: boolean;
|
forceEscape?: boolean;
|
||||||
showDetectedFields?: string[];
|
showDetectedFields?: string[];
|
||||||
|
showRowMenu?: boolean;
|
||||||
|
app?: CoreApp;
|
||||||
getRows: () => LogRowModel[];
|
getRows: () => LogRowModel[];
|
||||||
onClickFilterLabel?: (key: string, value: string) => void;
|
onClickFilterLabel?: (key: string, value: string) => void;
|
||||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||||
@ -52,6 +55,7 @@ interface Props extends Themeable2 {
|
|||||||
onClickShowDetectedField?: (key: string) => void;
|
onClickShowDetectedField?: (key: string) => void;
|
||||||
onClickHideDetectedField?: (key: string) => void;
|
onClickHideDetectedField?: (key: string) => void;
|
||||||
onLogRowHover?: (row?: LogRowModel) => void;
|
onLogRowHover?: (row?: LogRowModel) => void;
|
||||||
|
toggleContextIsOpen?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -91,6 +95,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
toggleContext = () => {
|
toggleContext = () => {
|
||||||
|
this.props.toggleContextIsOpen?.();
|
||||||
this.setState((state) => {
|
this.setState((state) => {
|
||||||
return {
|
return {
|
||||||
showContext: !state.showContext,
|
showContext: !state.showContext,
|
||||||
@ -132,6 +137,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
row,
|
row,
|
||||||
showDuplicates,
|
showDuplicates,
|
||||||
showContextToggle,
|
showContextToggle,
|
||||||
|
showRowMenu,
|
||||||
showLabels,
|
showLabels,
|
||||||
showTime,
|
showTime,
|
||||||
showDetectedFields,
|
showDetectedFields,
|
||||||
@ -141,6 +147,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
getFieldLinks,
|
getFieldLinks,
|
||||||
forceEscape,
|
forceEscape,
|
||||||
onLogRowHover,
|
onLogRowHover,
|
||||||
|
app,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { showDetails, showContext } = this.state;
|
const { showDetails, showContext } = this.state;
|
||||||
const style = getLogRowStyles(theme, row.logLevel);
|
const style = getLogRowStyles(theme, row.logLevel);
|
||||||
@ -207,9 +214,11 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
|||||||
context={context}
|
context={context}
|
||||||
contextIsOpen={showContext}
|
contextIsOpen={showContext}
|
||||||
showContextToggle={showContextToggle}
|
showContextToggle={showContextToggle}
|
||||||
|
showRowMenu={showRowMenu}
|
||||||
wrapLogMessage={wrapLogMessage}
|
wrapLogMessage={wrapLogMessage}
|
||||||
prettifyLogMessage={prettifyLogMessage}
|
prettifyLogMessage={prettifyLogMessage}
|
||||||
onToggleContext={this.toggleContext}
|
onToggleContext={this.toggleContext}
|
||||||
|
app={app}
|
||||||
logsSortOrder={logsSortOrder}
|
logsSortOrder={logsSortOrder}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -4,7 +4,7 @@ import React, { PureComponent } from 'react';
|
|||||||
import Highlighter from 'react-highlight-words';
|
import Highlighter from 'react-highlight-words';
|
||||||
import tinycolor from 'tinycolor2';
|
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 { withTheme2, Themeable2, IconButton, Tooltip } from '@grafana/ui';
|
||||||
|
|
||||||
import { LogMessageAnsi } from './LogMessageAnsi';
|
import { LogMessageAnsi } from './LogMessageAnsi';
|
||||||
@ -12,8 +12,6 @@ import { LogRowContext } from './LogRowContext';
|
|||||||
import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider';
|
import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider';
|
||||||
import { getLogRowStyles } from './getLogRowStyles';
|
import { getLogRowStyles } from './getLogRowStyles';
|
||||||
|
|
||||||
//Components
|
|
||||||
|
|
||||||
export const MAX_CHARACTERS = 100000;
|
export const MAX_CHARACTERS = 100000;
|
||||||
|
|
||||||
interface Props extends Themeable2 {
|
interface Props extends Themeable2 {
|
||||||
@ -24,6 +22,8 @@ interface Props extends Themeable2 {
|
|||||||
prettifyLogMessage: boolean;
|
prettifyLogMessage: boolean;
|
||||||
errors?: LogRowContextQueryErrors;
|
errors?: LogRowContextQueryErrors;
|
||||||
context?: LogRowContextRows;
|
context?: LogRowContextRows;
|
||||||
|
showRowMenu?: boolean;
|
||||||
|
app?: CoreApp;
|
||||||
showContextToggle?: (row?: LogRowModel) => boolean;
|
showContextToggle?: (row?: LogRowModel) => boolean;
|
||||||
getRows: () => LogRowModel[];
|
getRows: () => LogRowModel[];
|
||||||
onToggleContext: () => void;
|
onToggleContext: () => void;
|
||||||
@ -31,7 +31,7 @@ interface Props extends Themeable2 {
|
|||||||
logsSortOrder?: LogsSortOrder | null;
|
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();
|
const outlineColor = tinycolor(theme.components.dashboard.background).setAlpha(0.7).toRgbString();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -52,7 +52,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
display: block;
|
display: block;
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
`,
|
`,
|
||||||
contextButton: css`
|
rowMenu: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -60,15 +60,16 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -8px;
|
right: ${isInDashboard ? '0px' : '-8px'};
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
width: 80px;
|
|
||||||
height: 36px;
|
height: 36px;
|
||||||
background: ${theme.colors.background.primary};
|
background: ${theme.colors.background.primary};
|
||||||
box-shadow: ${theme.shadows.z3};
|
box-shadow: ${theme.shadows.z3};
|
||||||
padding: ${theme.spacing(0, 0, 0, 0.5)};
|
padding: ${theme.spacing(0, 0, 0, 0.5)};
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
visibility: hidden;
|
||||||
|
width: ${showContextButton ? '80px' : '40px'};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -125,17 +126,20 @@ class UnThemedLogRowMessage extends PureComponent<Props> {
|
|||||||
updateLimit,
|
updateLimit,
|
||||||
context,
|
context,
|
||||||
contextIsOpen,
|
contextIsOpen,
|
||||||
showContextToggle,
|
showRowMenu,
|
||||||
wrapLogMessage,
|
wrapLogMessage,
|
||||||
prettifyLogMessage,
|
prettifyLogMessage,
|
||||||
onToggleContext,
|
onToggleContext,
|
||||||
|
app,
|
||||||
logsSortOrder,
|
logsSortOrder,
|
||||||
|
showContextToggle,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const style = getLogRowStyles(theme, row.logLevel);
|
const style = getLogRowStyles(theme, row.logLevel);
|
||||||
const { hasAnsi, raw } = row;
|
const { hasAnsi, raw } = row;
|
||||||
const restructuredEntry = restructureLog(raw, prettifyLogMessage);
|
const restructuredEntry = restructureLog(raw, prettifyLogMessage);
|
||||||
const styles = getStyles(theme);
|
const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false;
|
||||||
|
const styles = getStyles(theme, shouldShowContextToggle, app === CoreApp.Dashboard);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// When context is open, the position has to be NOT relative.
|
// 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 })}>
|
<span className={cx(styles.positionRelative, { [styles.rowWithContext]: contextIsOpen })}>
|
||||||
{renderLogMessage(hasAnsi, restructuredEntry, row.searchWords, style.logsRowMatchHighLight)}
|
{renderLogMessage(hasAnsi, restructuredEntry, row.searchWords, style.logsRowMatchHighLight)}
|
||||||
</span>
|
</span>
|
||||||
{!contextIsOpen && showContextToggle?.(row) && (
|
{showRowMenu && (
|
||||||
<span
|
<span className={cx('log-row-menu', styles.rowMenu)} onClick={(e) => e.stopPropagation()}>
|
||||||
className={cx('log-row-context', style.context, styles.contextButton)}
|
{shouldShowContextToggle && (
|
||||||
onClick={(e) => e.stopPropagation()}
|
<Tooltip placement="top" content={'Show context'}>
|
||||||
>
|
<IconButton size="md" name="gf-show-context" onClick={this.onContextToggle} />
|
||||||
<Tooltip placement="top" content={'Show context'}>
|
</Tooltip>
|
||||||
<IconButton size="md" name="gf-show-context" onClick={this.onContextToggle} />
|
)}
|
||||||
</Tooltip>
|
|
||||||
<Tooltip placement="top" content={'Copy'}>
|
<Tooltip placement="top" content={'Copy'}>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="md"
|
size="md"
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
import memoizeOne from 'memoize-one';
|
import memoizeOne from 'memoize-one';
|
||||||
import React, { PureComponent } from 'react';
|
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';
|
import { withTheme2, Themeable2 } from '@grafana/ui';
|
||||||
|
|
||||||
//Components
|
//Components
|
||||||
@ -25,6 +34,7 @@ export interface Props extends Themeable2 {
|
|||||||
previewLimit?: number;
|
previewLimit?: number;
|
||||||
forceEscape?: boolean;
|
forceEscape?: boolean;
|
||||||
showDetectedFields?: string[];
|
showDetectedFields?: string[];
|
||||||
|
app?: CoreApp;
|
||||||
showContextToggle?: (row?: LogRowModel) => boolean;
|
showContextToggle?: (row?: LogRowModel) => boolean;
|
||||||
onClickFilterLabel?: (key: string, value: string) => void;
|
onClickFilterLabel?: (key: string, value: string) => void;
|
||||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||||
@ -37,6 +47,7 @@ export interface Props extends Themeable2 {
|
|||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
renderAll: boolean;
|
renderAll: boolean;
|
||||||
|
contextIsOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnThemedLogRows extends PureComponent<Props, State> {
|
class UnThemedLogRows extends PureComponent<Props, State> {
|
||||||
@ -48,6 +59,18 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
renderAll: false,
|
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() {
|
componentDidMount() {
|
||||||
@ -100,8 +123,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
onClickHideDetectedField,
|
onClickHideDetectedField,
|
||||||
forceEscape,
|
forceEscape,
|
||||||
onLogRowHover,
|
onLogRowHover,
|
||||||
|
app,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { renderAll } = this.state;
|
const { renderAll, contextIsOpen } = this.state;
|
||||||
const { logsRowsTable } = getLogRowStyles(theme);
|
const { logsRowsTable } = getLogRowStyles(theme);
|
||||||
const dedupedRows = deduplicatedRows ? deduplicatedRows : logRows;
|
const dedupedRows = deduplicatedRows ? deduplicatedRows : logRows;
|
||||||
const hasData = logRows && logRows.length > 0;
|
const hasData = logRows && logRows.length > 0;
|
||||||
@ -130,6 +154,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
getRowContext={getRowContext}
|
getRowContext={getRowContext}
|
||||||
row={row}
|
row={row}
|
||||||
showContextToggle={showContextToggle}
|
showContextToggle={showContextToggle}
|
||||||
|
showRowMenu={!contextIsOpen}
|
||||||
showDuplicates={showDuplicates}
|
showDuplicates={showDuplicates}
|
||||||
showLabels={showLabels}
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
@ -145,7 +170,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
getFieldLinks={getFieldLinks}
|
getFieldLinks={getFieldLinks}
|
||||||
logsSortOrder={logsSortOrder}
|
logsSortOrder={logsSortOrder}
|
||||||
forceEscape={forceEscape}
|
forceEscape={forceEscape}
|
||||||
|
toggleContextIsOpen={this.toggleContextIsOpen}
|
||||||
onLogRowHover={onLogRowHover}
|
onLogRowHover={onLogRowHover}
|
||||||
|
app={app}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{hasData &&
|
{hasData &&
|
||||||
@ -157,6 +184,7 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
getRowContext={getRowContext}
|
getRowContext={getRowContext}
|
||||||
row={row}
|
row={row}
|
||||||
showContextToggle={showContextToggle}
|
showContextToggle={showContextToggle}
|
||||||
|
showRowMenu={!contextIsOpen}
|
||||||
showDuplicates={showDuplicates}
|
showDuplicates={showDuplicates}
|
||||||
showLabels={showLabels}
|
showLabels={showLabels}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
@ -172,7 +200,9 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
|||||||
getFieldLinks={getFieldLinks}
|
getFieldLinks={getFieldLinks}
|
||||||
logsSortOrder={logsSortOrder}
|
logsSortOrder={logsSortOrder}
|
||||||
forceEscape={forceEscape}
|
forceEscape={forceEscape}
|
||||||
|
toggleContextIsOpen={this.toggleContextIsOpen}
|
||||||
onLogRowHover={onLogRowHover}
|
onLogRowHover={onLogRowHover}
|
||||||
|
app={app}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{hasData && !renderAll && (
|
{hasData && !renderAll && (
|
||||||
|
@ -45,13 +45,6 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => {
|
|||||||
font-size: ${theme.typography.bodySmall.fontSize};
|
font-size: ${theme.typography.bodySmall.fontSize};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`,
|
`,
|
||||||
context: css`
|
|
||||||
label: context;
|
|
||||||
visibility: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 10px;
|
|
||||||
`,
|
|
||||||
logsRow: css`
|
logsRow: css`
|
||||||
label: logs-row;
|
label: logs-row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -59,13 +52,9 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.log-row-context {
|
.log-row-menu {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
text-decoration: underline;
|
|
||||||
&:hover {
|
|
||||||
color: ${theme.colors.warning.main};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
LogRowModel,
|
LogRowModel,
|
||||||
DataHoverClearEvent,
|
DataHoverClearEvent,
|
||||||
DataHoverEvent,
|
DataHoverEvent,
|
||||||
|
CoreApp,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { CustomScrollbar, useStyles2, usePanelContext } from '@grafana/ui';
|
import { CustomScrollbar, useStyles2, usePanelContext } from '@grafana/ui';
|
||||||
import { dataFrameToLogsModel, dedupLogRows, COMMON_LABELS } from 'app/core/logsModel';
|
import { dataFrameToLogsModel, dedupLogRows, COMMON_LABELS } from 'app/core/logsModel';
|
||||||
@ -116,6 +117,7 @@ export const LogsPanel: React.FunctionComponent<LogsPanelProps> = ({
|
|||||||
enableLogDetails={enableLogDetails}
|
enableLogDetails={enableLogDetails}
|
||||||
previewLimit={isAscending ? logRows.length : undefined}
|
previewLimit={isAscending ? logRows.length : undefined}
|
||||||
onLogRowHover={onLogRowHover}
|
onLogRowHover={onLogRowHover}
|
||||||
|
app={CoreApp.Dashboard}
|
||||||
/>
|
/>
|
||||||
{showCommonLabels && isAscending && renderCommonLabels()}
|
{showCommonLabels && isAscending && renderCommonLabels()}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user