@ -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 = { truncat edE ntry} highlight = { highlight } / > ;
} else if ( needsHighlighter ) {
return (
< Highlighter
textToHighlight = { entry }
textToHighlight = { truncat edE ntry}
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 ,