|
|
|
@ -1,13 +1,14 @@ |
|
|
|
|
import { css } from '@emotion/css'; |
|
|
|
|
import { useCallback, useMemo, MouseEvent, useRef, ChangeEvent } from 'react'; |
|
|
|
|
|
|
|
|
|
import { colorManipulator, GrafanaTheme2, LogRowModel } from '@grafana/data'; |
|
|
|
|
import { colorManipulator, GrafanaTheme2, LogRowModel, store } from '@grafana/data'; |
|
|
|
|
import { t } from '@grafana/i18n'; |
|
|
|
|
import { IconButton, Input, useStyles2 } from '@grafana/ui'; |
|
|
|
|
|
|
|
|
|
import { copyText, handleOpenLogsContextClick } from '../../utils'; |
|
|
|
|
import { LOG_LINE_BODY_FIELD_NAME } from '../LogDetailsBody'; |
|
|
|
|
|
|
|
|
|
import { LogLineDetailsMode } from './LogLineDetails'; |
|
|
|
|
import { useLogIsPinned, useLogListContext } from './LogListContext'; |
|
|
|
|
import { LogListModel } from './processing'; |
|
|
|
|
|
|
|
|
@ -20,18 +21,22 @@ interface Props { |
|
|
|
|
export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => { |
|
|
|
|
const { |
|
|
|
|
closeDetails, |
|
|
|
|
detailsMode, |
|
|
|
|
displayedFields, |
|
|
|
|
getRowContextQuery, |
|
|
|
|
logOptionsStorageKey, |
|
|
|
|
logSupportsContext, |
|
|
|
|
setDetailsMode, |
|
|
|
|
onClickHideField, |
|
|
|
|
onClickShowField, |
|
|
|
|
onOpenContext, |
|
|
|
|
onPermalinkClick, |
|
|
|
|
onPinLine, |
|
|
|
|
onUnpinLine, |
|
|
|
|
wrapLogMessage, |
|
|
|
|
} = useLogListContext(); |
|
|
|
|
const pinned = useLogIsPinned(log); |
|
|
|
|
const styles = useStyles2(getStyles); |
|
|
|
|
const styles = useStyles2(getStyles, detailsMode, wrapLogMessage); |
|
|
|
|
const containerRef = useRef<HTMLDivElement | null>(null); |
|
|
|
|
const inputRef = useRef<HTMLInputElement | null>(null); |
|
|
|
|
|
|
|
|
@ -66,6 +71,15 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => { |
|
|
|
|
const showLogLineToggle = onClickHideField && onClickShowField && displayedFields.length > 0; |
|
|
|
|
const logLineDisplayed = displayedFields.includes(LOG_LINE_BODY_FIELD_NAME); |
|
|
|
|
|
|
|
|
|
const toggleDetailsMode = useCallback(() => { |
|
|
|
|
const newMode = detailsMode === 'inline' ? 'sidebar' : 'inline'; |
|
|
|
|
if (logOptionsStorageKey) { |
|
|
|
|
store.set(`${logOptionsStorageKey}.detailsMode`, newMode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setDetailsMode(newMode); |
|
|
|
|
}, [detailsMode, logOptionsStorageKey, setDetailsMode]); |
|
|
|
|
|
|
|
|
|
const toggleLogLine = useCallback(() => { |
|
|
|
|
if (logLineDisplayed) { |
|
|
|
|
onClickHideField?.(LOG_LINE_BODY_FIELD_NAME); |
|
|
|
@ -121,65 +135,76 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => { |
|
|
|
|
variant={logLineDisplayed ? 'primary' : undefined} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
<IconButton |
|
|
|
|
tooltip={t('logs.log-line-details.copy-to-clipboard', 'Copy to clipboard')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
size="md" |
|
|
|
|
name="copy" |
|
|
|
|
onClick={copyLogLine} |
|
|
|
|
tabIndex={0} |
|
|
|
|
/> |
|
|
|
|
{onPermalinkClick && log.rowId !== undefined && log.uid && ( |
|
|
|
|
<div className={styles.icons}> |
|
|
|
|
<IconButton |
|
|
|
|
tooltip={t('logs.log-line-details.copy-shortlink', 'Copy shortlink')} |
|
|
|
|
tooltip={t('logs.log-line-details.copy-to-clipboard', 'Copy to clipboard')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
size="md" |
|
|
|
|
name="share-alt" |
|
|
|
|
onClick={copyLinkToLogLine} |
|
|
|
|
tabIndex={0} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
{pinned && onUnpinLine && ( |
|
|
|
|
<IconButton |
|
|
|
|
size="md" |
|
|
|
|
name="gf-pin" |
|
|
|
|
onClick={togglePinning} |
|
|
|
|
tooltip={t('logs.log-line-details.unpin-line', 'Unpin log')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
name="copy" |
|
|
|
|
onClick={copyLogLine} |
|
|
|
|
tabIndex={0} |
|
|
|
|
variant="primary" |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
{!pinned && onPinLine && ( |
|
|
|
|
{onPermalinkClick && log.rowId !== undefined && log.uid && ( |
|
|
|
|
<IconButton |
|
|
|
|
tooltip={t('logs.log-line-details.copy-shortlink', 'Copy shortlink')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
size="md" |
|
|
|
|
name="share-alt" |
|
|
|
|
onClick={copyLinkToLogLine} |
|
|
|
|
tabIndex={0} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
{pinned && onUnpinLine && ( |
|
|
|
|
<IconButton |
|
|
|
|
size="md" |
|
|
|
|
name="gf-pin" |
|
|
|
|
onClick={togglePinning} |
|
|
|
|
tooltip={t('logs.log-line-details.unpin-line', 'Unpin log')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
tabIndex={0} |
|
|
|
|
variant="primary" |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
{!pinned && onPinLine && ( |
|
|
|
|
<IconButton |
|
|
|
|
size="md" |
|
|
|
|
name="gf-pin" |
|
|
|
|
onClick={togglePinning} |
|
|
|
|
tooltip={t('logs.log-line-details.pin-line', 'Pin log')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
tabIndex={0} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
{shouldlogSupportsContext && ( |
|
|
|
|
<IconButton |
|
|
|
|
size="md" |
|
|
|
|
name="gf-show-context" |
|
|
|
|
onClick={showContext} |
|
|
|
|
tooltip={t('logs.log-line-details.show-context', 'Show context')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
tabIndex={0} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
<IconButton |
|
|
|
|
size="md" |
|
|
|
|
name="gf-pin" |
|
|
|
|
onClick={togglePinning} |
|
|
|
|
tooltip={t('logs.log-line-details.pin-line', 'Pin log')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
tabIndex={0} |
|
|
|
|
name={detailsMode === 'inline' ? 'columns' : 'gf-layout-simple'} |
|
|
|
|
tooltip={ |
|
|
|
|
detailsMode === 'inline' |
|
|
|
|
? t('logs.log-line-details.sidebar-mode', 'Anchor to the right') |
|
|
|
|
: t('logs.log-line-details.inline-mode', 'Display inline') |
|
|
|
|
} |
|
|
|
|
onClick={toggleDetailsMode} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
{shouldlogSupportsContext && ( |
|
|
|
|
<IconButton |
|
|
|
|
size="md" |
|
|
|
|
name="gf-show-context" |
|
|
|
|
onClick={showContext} |
|
|
|
|
tooltip={t('logs.log-line-details.show-context', 'Show context')} |
|
|
|
|
tooltipPlacement="top" |
|
|
|
|
tabIndex={0} |
|
|
|
|
name="times" |
|
|
|
|
aria-label={t('logs.log-line-details.close', 'Close log details')} |
|
|
|
|
onClick={closeDetails} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
<IconButton |
|
|
|
|
name="times" |
|
|
|
|
aria-label={t('logs.log-line-details.close', 'Close log details')} |
|
|
|
|
onClick={closeDetails} |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const getStyles = (theme: GrafanaTheme2) => ({ |
|
|
|
|
const getStyles = (theme: GrafanaTheme2, mode: LogLineDetailsMode, wrapLogMessage: boolean) => ({ |
|
|
|
|
container: css({ |
|
|
|
|
overflow: 'auto', |
|
|
|
|
height: '100%', |
|
|
|
@ -192,7 +217,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ |
|
|
|
|
alignItems: 'center', |
|
|
|
|
background: theme.colors.background.canvas, |
|
|
|
|
display: 'flex', |
|
|
|
|
flexDirection: 'row', |
|
|
|
|
flexDirection: !wrapLogMessage && mode === 'inline' ? 'row-reverse' : 'row', |
|
|
|
|
gap: theme.spacing(0.75), |
|
|
|
|
zIndex: theme.zIndex.navbarFixed, |
|
|
|
|
height: theme.spacing(5.5), |
|
|
|
@ -201,6 +226,10 @@ const getStyles = (theme: GrafanaTheme2) => ({ |
|
|
|
|
position: 'sticky', |
|
|
|
|
top: 0, |
|
|
|
|
}), |
|
|
|
|
icons: css({ |
|
|
|
|
display: 'flex', |
|
|
|
|
gap: theme.spacing(0.75), |
|
|
|
|
}), |
|
|
|
|
copyLogButton: css({ |
|
|
|
|
padding: 0, |
|
|
|
|
height: theme.spacing(4), |
|
|
|
|