diff --git a/public/app/features/logs/components/LogDetails.tsx b/public/app/features/logs/components/LogDetails.tsx index 809ad3fbe1e..a4d43df0ac6 100644 --- a/public/app/features/logs/components/LogDetails.tsx +++ b/public/app/features/logs/components/LogDetails.tsx @@ -7,11 +7,9 @@ import { Themeable2, withTheme2 } from '@grafana/ui'; import { calculateLogsLabelStats, calculateStats } from '../utils'; import { LogDetailsRow } from './LogDetailsRow'; -import { getLogRowStyles } from './getLogRowStyles'; +import { getLogLevelStyles, getLogRowStyles } from './getLogRowStyles'; import { getAllFields } from './logParser'; -//Components - export interface Props extends Themeable2 { row: LogRowModel; showDuplicates: boolean; @@ -66,7 +64,8 @@ class UnThemedLogDetails extends PureComponent { getFieldLinks, wrapLogMessage, } = this.props; - const style = getLogRowStyles(theme, row.logLevel); + const rowStyles = getLogRowStyles(theme); + const levelStyles = getLogLevelStyles(theme, row.logLevel); const styles = getStyles(theme); const labels = row.labels ? row.labels : {}; const labelsAvailable = Object.keys(labels).length > 0; @@ -77,19 +76,21 @@ class UnThemedLogDetails extends PureComponent { const linksAvailable = links && links.length > 0; // If logs with error, we are not showing the level color - const levelClassName = cx(!hasError && [style.logsRowLevel, styles.logsRowLevelDetails]); + const levelClassName = hasError + ? '' + : `${levelStyles.logsRowLevelColor} ${rowStyles.logsRowLevel} ${styles.logsRowLevelDetails}`; return ( {showDuplicates && } -
- +
+
{(labelsAvailable || fieldsAvailable) && ( - @@ -138,7 +139,7 @@ class UnThemedLogDetails extends PureComponent { {linksAvailable && ( - diff --git a/public/app/features/logs/components/LogRow.tsx b/public/app/features/logs/components/LogRow.tsx index ac0813f6b6d..170855ffacf 100644 --- a/public/app/features/logs/components/LogRow.tsx +++ b/public/app/features/logs/components/LogRow.tsx @@ -1,4 +1,4 @@ -import { cx, css } from '@emotion/css'; +import { cx } from '@emotion/css'; import React, { PureComponent } from 'react'; import { @@ -6,16 +6,15 @@ import { LinkModel, LogRowModel, LogsSortOrder, - TimeZone, DataQueryResponse, dateTimeFormat, - GrafanaTheme2, CoreApp, DataFrame, DataSourceWithLogsContextSupport, } from '@grafana/data'; import { reportInteraction } from '@grafana/runtime'; -import { styleMixins, withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui'; +import { TimeZone } from '@grafana/schema'; +import { withTheme2, Themeable2, Icon, Tooltip } from '@grafana/ui'; import { checkLogsError, escapeUnescapedString } from '../utils'; @@ -30,9 +29,7 @@ import { } from './LogRowContextProvider'; import { LogRowMessage } from './LogRowMessage'; import { LogRowMessageDisplayedFields } from './LogRowMessageDisplayedFields'; -import { getLogRowStyles } from './getLogRowStyles'; - -//Components +import { getLogLevelStyles, LogRowStyles } from './getLogRowStyles'; interface Props extends Themeable2 { row: LogRowModel; @@ -61,6 +58,7 @@ interface Props extends Themeable2 { onClickHideField?: (key: string) => void; onLogRowHover?: (row?: LogRowModel) => void; toggleContextIsOpen?: () => void; + styles: LogRowStyles; } interface State { @@ -68,24 +66,6 @@ interface State { showDetails: boolean; } -const getStyles = (theme: GrafanaTheme2) => { - return { - topVerticalAlign: css` - label: topVerticalAlign; - margin-top: -${theme.spacing(0.9)}; - margin-left: -${theme.spacing(0.25)}; - `, - detailsOpen: css` - &:hover { - background-color: ${styleMixins.hoverColor(theme.colors.background.primary, theme)}; - } - `, - errorLogRow: css` - label: erroredLogRow; - color: ${theme.colors.text.secondary}; - `, - }; -}; /** * Renders a log line. * @@ -171,14 +151,14 @@ class UnThemedLogRow extends PureComponent { onLogRowHover, app, scrollElement, + styles, } = this.props; const { showDetails, showContext } = this.state; - const style = getLogRowStyles(theme, row.logLevel); - const styles = getStyles(theme); + const levelStyles = getLogLevelStyles(theme, row.logLevel); const { errorMessage, hasError } = checkLogsError(row); - const logRowBackground = cx(style.logsRow, { + const logRowBackground = cx(styles.logsRow, { [styles.errorLogRow]: hasError, - [style.contextBackground]: showContext, + [styles.contextBackground]: showContext, }); const processedRow = @@ -199,25 +179,25 @@ class UnThemedLogRow extends PureComponent { }} > {showDuplicates && ( - )} - {enableLogDetails && ( - )} - {showTime && } + {showTime && } {showLabels && processedRow.uniqueLabels && ( - )} @@ -247,6 +227,7 @@ class UnThemedLogRow extends PureComponent { app={app} scrollElement={scrollElement} logsSortOrder={logsSortOrder} + styles={styles} /> )} diff --git a/public/app/features/logs/components/LogRowMessage.tsx b/public/app/features/logs/components/LogRowMessage.tsx index 380e32feb05..1671f30a066 100644 --- a/public/app/features/logs/components/LogRowMessage.tsx +++ b/public/app/features/logs/components/LogRowMessage.tsx @@ -1,27 +1,25 @@ -import { css, cx } from '@emotion/css'; +import { cx } from '@emotion/css'; import memoizeOne from 'memoize-one'; import React, { PureComponent } from 'react'; import Highlighter from 'react-highlight-words'; -import tinycolor from 'tinycolor2'; import { LogRowModel, findHighlightChunksInText, - GrafanaTheme2, LogsSortOrder, CoreApp, DataSourceWithLogsContextSupport, } from '@grafana/data'; -import { withTheme2, Themeable2, IconButton, Tooltip } from '@grafana/ui'; +import { IconButton, Tooltip } from '@grafana/ui'; import { LogMessageAnsi } from './LogMessageAnsi'; import { LogRowContext } from './LogRowContext'; import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider'; -import { getLogRowStyles } from './getLogRowStyles'; +import { LogRowStyles } from './getLogRowStyles'; export const MAX_CHARACTERS = 100000; -interface Props extends Themeable2 { +interface Props { row: LogRowModel; hasMoreContextRows?: HasMoreContextRows; contextIsOpen: boolean; @@ -39,67 +37,9 @@ interface Props extends Themeable2 { updateLimit?: () => void; runContextQuery?: () => void; logsSortOrder?: LogsSortOrder | null; + styles: LogRowStyles; } -const getStyles = (theme: GrafanaTheme2, showContextButton: boolean, isInExplore: boolean) => { - const outlineColor = tinycolor(theme.components.dashboard.background).setAlpha(0.7).toRgbString(); - - return { - positionRelative: css` - label: positionRelative; - position: relative; - `, - rowWithContext: css` - label: rowWithContext; - z-index: 1; - outline: 9999px solid ${outlineColor}; - display: inherit; - `, - horizontalScroll: css` - label: horizontalScroll; - white-space: pre; - `, - contextNewline: css` - display: block; - margin-left: 0px; - `, - rowMenu: css` - display: flex; - flex-wrap: nowrap; - flex-direction: row; - align-content: flex-end; - justify-content: space-evenly; - align-items: center; - position: absolute; - top: 0; - bottom: auto; - height: ${theme.spacing(4.5)}; - 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 ? theme.spacing(10) : theme.spacing(5)}; - `, - logRowMenuCell: css` - position: absolute; - right: ${!isInExplore ? '40px' : `calc(75px + ${theme.spacing()} + ${showContextButton ? '80px' : '40px'})`}; - margin-top: -${theme.spacing(0.125)}; - `, - logLine: css` - background-color: transparent; - border: none; - diplay: inline; - font-family: ${theme.typography.fontFamilyMonospace}; - font-size: ${theme.typography.bodySmall.fontSize}; - letter-spacing: ${theme.typography.bodySmall.letterSpacing}; - text-align: left; - padding: 0; - user-select: text; - `, - }; -}; - function renderLogMessage( hasAnsi: boolean, entry: string, @@ -137,7 +77,7 @@ const restructureLog = memoizeOne((line: string, prettifyLogMessage: boolean): s return line; }); -class UnThemedLogRowMessage extends PureComponent { +export class LogRowMessage extends PureComponent { logRowRef: React.RefObject = React.createRef(); onContextToggle = (e: React.SyntheticEvent) => { @@ -159,7 +99,6 @@ class UnThemedLogRowMessage extends PureComponent { render() { const { row, - theme, errors, hasMoreContextRows, updateLimit, @@ -174,24 +113,23 @@ class UnThemedLogRowMessage extends PureComponent { logsSortOrder, showContextToggle, getLogRowContextUi, + styles, } = this.props; - - const style = getLogRowStyles(theme, row.logLevel); const { hasAnsi, raw } = row; const restructuredEntry = restructureLog(raw, prettifyLogMessage); const shouldShowContextToggle = showContextToggle ? showContextToggle(row) : false; - const styles = getStyles(theme, shouldShowContextToggle, app === CoreApp.Explore); + const inExplore = app === CoreApp.Explore; return ( <> { // When context is open, the position has to be NOT relative. // Setting the postion as inline-style to - // overwrite the more sepecific style definition from `style.logsRowMessage`. + // overwrite the more sepecific style definition from `styles.logsRowMessage`. } {showRowMenu && ( -
+ Fields
+ Links
+ {processedRow.duplicates && processedRow.duplicates > 0 ? `${processedRow.duplicates + 1}x` : null} + {hasError && ( - + )} + {this.renderTimeStamp(row.timeEpochMs)}{this.renderTimeStamp(row.timeEpochMs)} +
{ /> )}
- e.stopPropagation()}> + + e.stopPropagation()} + > {shouldShowContextToggle && ( @@ -240,6 +189,3 @@ class UnThemedLogRowMessage extends PureComponent { ); } } - -export const LogRowMessage = withTheme2(UnThemedLogRowMessage); -LogRowMessage.displayName = 'LogRowMessage'; diff --git a/public/app/features/logs/components/LogRows.tsx b/public/app/features/logs/components/LogRows.tsx index 3ebab8bb90f..464e37837a7 100644 --- a/public/app/features/logs/components/LogRows.tsx +++ b/public/app/features/logs/components/LogRows.tsx @@ -133,7 +133,7 @@ class UnThemedLogRows extends PureComponent { getLogRowContextUi, } = this.props; const { renderAll, contextIsOpen } = this.state; - const { logsRowsTable } = getLogRowStyles(theme); + const styles = getLogRowStyles(theme); const dedupedRows = deduplicatedRows ? deduplicatedRows : logRows; const hasData = logRows && logRows.length > 0; const dedupCount = dedupedRows @@ -151,7 +151,7 @@ class UnThemedLogRows extends PureComponent { const getRowContext = this.props.getRowContext ? this.props.getRowContext : () => Promise.resolve([]); return ( - +
{hasData && firstRows.map((row, index) => ( @@ -182,6 +182,7 @@ class UnThemedLogRows extends PureComponent { onLogRowHover={onLogRowHover} app={app} scrollElement={scrollElement} + styles={styles} /> ))} {hasData && @@ -214,6 +215,7 @@ class UnThemedLogRows extends PureComponent { onLogRowHover={onLogRowHover} app={app} scrollElement={scrollElement} + styles={styles} /> ))} {hasData && !renderAll && ( diff --git a/public/app/features/logs/components/getLogRowStyles.ts b/public/app/features/logs/components/getLogRowStyles.ts index 92db16e4353..526e19d8997 100644 --- a/public/app/features/logs/components/getLogRowStyles.ts +++ b/public/app/features/logs/components/getLogRowStyles.ts @@ -1,12 +1,11 @@ import { css } from '@emotion/css'; +import tinycolor from 'tinycolor2'; import { GrafanaTheme2, LogLevel } from '@grafana/data'; import { styleMixins } from '@grafana/ui'; -export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => { +export const getLogLevelStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => { let logColor = theme.isLight ? theme.v1.palette.gray5 : theme.v1.palette.gray2; - const hoverBgColor = styleMixins.hoverColor(theme.colors.background.secondary, theme); - switch (logLevel) { case LogLevel.crit: case LogLevel.critical: @@ -32,6 +31,32 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => { } return { + logsRowLevelColor: css` + &::after { + background-color: ${logColor}; + } + `, + }; +}; + +export const getLogRowStyles = (theme: GrafanaTheme2) => { + const hoverBgColor = styleMixins.hoverColor(theme.colors.background.secondary, theme); + const contextOutlineColor = tinycolor(theme.components.dashboard.background).setAlpha(0.7).toRgbString(); + return { + logsRowLevel: css` + label: logs-row__level; + max-width: ${theme.spacing(1.25)}; + cursor: default; + &::after { + content: ''; + display: block; + position: absolute; + top: 1px; + bottom: 1px; + width: 3px; + left: ${theme.spacing(0.5)}; + } + `, logsRowMatchHighLight: css` label: logs-row__match-highlight; background: inherit; @@ -81,21 +106,6 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => { width: 4em; cursor: default; `, - logsRowLevel: css` - label: logs-row__level; - max-width: ${theme.spacing(1.25)}; - cursor: default; - &::after { - content: ''; - display: block; - position: absolute; - top: 1px; - bottom: 1px; - width: 3px; - left: ${theme.spacing(0.5)}; - background-color: ${logColor}; - } - `, logIconError: css` color: ${theme.colors.warning.main}; `, @@ -174,5 +184,86 @@ export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => { background-color: ${hoverBgColor}; } `, + // Log row + topVerticalAlign: css` + label: topVerticalAlign; + margin-top: -${theme.spacing(0.9)}; + margin-left: -${theme.spacing(0.25)}; + `, + detailsOpen: css` + &:hover { + background-color: ${styleMixins.hoverColor(theme.colors.background.primary, theme)}; + } + `, + errorLogRow: css` + label: erroredLogRow; + color: ${theme.colors.text.secondary}; + `, + // Log Row Message + positionRelative: css` + label: positionRelative; + position: relative; + `, + rowWithContext: css` + label: rowWithContext; + z-index: 1; + outline: 9999px solid ${contextOutlineColor}; + display: inherit; + `, + horizontalScroll: css` + label: horizontalScroll; + white-space: pre; + `, + contextNewline: css` + display: block; + margin-left: 0px; + `, + rowMenu: css` + display: flex; + flex-wrap: nowrap; + flex-direction: row; + align-content: flex-end; + justify-content: space-evenly; + align-items: center; + position: absolute; + top: 0; + bottom: auto; + height: ${theme.spacing(4.5)}; + background: ${theme.colors.background.primary}; + box-shadow: ${theme.shadows.z3}; + padding: ${theme.spacing(0, 0, 0, 0.5)}; + z-index: 100; + visibility: hidden; + width: ${theme.spacing(5)}; + `, + rowMenuWithContextButton: css` + width: ${theme.spacing(10)}; + `, + logRowMenuCell: css` + position: absolute; + margin-top: -${theme.spacing(0.125)}; + `, + logRowMenuCellDefaultPosition: css` + right: 40px; + `, + logRowMenuCellExplore: css` + right: calc(115px + ${theme.spacing(1)}); + `, + logRowMenuCellExploreWithContextButton: css` + right: calc(155px + ${theme.spacing(1)}); + `, + logLine: css` + background-color: transparent; + border: none; + diplay: inline; + font-family: ${theme.typography.fontFamilyMonospace}; + font-size: ${theme.typography.bodySmall.fontSize}; + letter-spacing: ${theme.typography.bodySmall.letterSpacing}; + text-align: left; + padding: 0; + user-select: text; + `, }; }; + +export type LogRowStyles = ReturnType;