diff --git a/public/app/features/logs/components/LogRow.tsx b/public/app/features/logs/components/LogRow.tsx index 53277c3f1de..616820d3165 100644 --- a/public/app/features/logs/components/LogRow.tsx +++ b/public/app/features/logs/components/LogRow.tsx @@ -119,7 +119,8 @@ class UnThemedLogRow extends PureComponent { context?: LogRowContextRows, errors?: LogRowContextQueryErrors, hasMoreContextRows?: HasMoreContextRows, - updateLimit?: () => void + updateLimit?: () => void, + logsSortOrder?: LogsSortOrder | null ) { const { getRows, @@ -209,6 +210,7 @@ class UnThemedLogRow extends PureComponent { wrapLogMessage={wrapLogMessage} prettifyLogMessage={prettifyLogMessage} onToggleContext={this.toggleContext} + logsSortOrder={logsSortOrder} /> )} @@ -240,8 +242,8 @@ class UnThemedLogRow extends PureComponent { return ( <> - {({ result, errors, hasMoreContextRows, updateLimit }) => { - return <>{this.renderLogRow(result, errors, hasMoreContextRows, updateLimit)}; + {({ result, errors, hasMoreContextRows, updateLimit, logsSortOrder }) => { + return <>{this.renderLogRow(result, errors, hasMoreContextRows, updateLimit, logsSortOrder)}; }} diff --git a/public/app/features/logs/components/LogRowContext.test.tsx b/public/app/features/logs/components/LogRowContext.test.tsx index b63d7f8f791..40e43b01b02 100644 --- a/public/app/features/logs/components/LogRowContext.test.tsx +++ b/public/app/features/logs/components/LogRowContext.test.tsx @@ -1,9 +1,9 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; -import { LogRowModel } from '@grafana/data'; +import { LogRowModel, LogsSortOrder } from '@grafana/data'; -import { LogRowContextGroup } from './LogRowContext'; +import { LogGroupPosition, LogRowContextGroup } from './LogRowContext'; describe('LogRowContextGroup component', () => { it('should correctly render logs with ANSI', () => { @@ -13,13 +13,31 @@ describe('LogRowContextGroup component', () => { canLoadMoreRows: false, row: {} as LogRowModel, className: '', + groupPosition: LogGroupPosition.Top, }; - render( -
- -
- ); + render(); expect(screen.getAllByTestId('ansiLogLine')).toHaveLength(2); }); + + it.each([ + [LogGroupPosition.Top, LogsSortOrder.Ascending, 'before'], + [LogGroupPosition.Top, LogsSortOrder.Descending, 'after'], + [LogGroupPosition.Bottom, LogsSortOrder.Ascending, 'after'], + [LogGroupPosition.Bottom, LogsSortOrder.Descending, 'before'], + ])(`should when component is %s and sorting is %s display '%s'`, async (groupPosition, logsSortOrder, expected) => { + const defaultProps = { + rows: ['Log 1', 'Log 2', 'Log 3'], + onLoadMoreContext: () => {}, + canLoadMoreRows: false, + row: {} as LogRowModel, + className: '', + groupPosition, + logsSortOrder, + }; + + render(); + + expect(await screen.findByText(`Showing 3 lines ${expected} match.`)).toBeInTheDocument(); + }); }); diff --git a/public/app/features/logs/components/LogRowContext.tsx b/public/app/features/logs/components/LogRowContext.tsx index 038914f2e4f..27ab94d4080 100644 --- a/public/app/features/logs/components/LogRowContext.tsx +++ b/public/app/features/logs/components/LogRowContext.tsx @@ -1,18 +1,24 @@ import { css, cx } from '@emotion/css'; import React, { useRef, useState, useLayoutEffect, useEffect } from 'react'; -import { GrafanaTheme2, DataQueryError, LogRowModel, textUtil } from '@grafana/data'; +import { GrafanaTheme2, DataQueryError, LogRowModel, textUtil, LogsSortOrder } from '@grafana/data'; import { useStyles2, Alert, ClickOutsideWrapper, CustomScrollbar, List } from '@grafana/ui'; import { LogMessageAnsi } from './LogMessageAnsi'; import { LogRowContextRows, LogRowContextQueryErrors, HasMoreContextRows } from './LogRowContextProvider'; +export enum LogGroupPosition { + Bottom = 'bottom', + Top = 'top', +} + interface LogRowContextProps { row: LogRowModel; context: LogRowContextRows; wrapLogMessage: boolean; errors?: LogRowContextQueryErrors; hasMoreContextRows?: HasMoreContextRows; + logsSortOrder?: LogsSortOrder | null; onOutsideClick: () => void; onLoadMoreContext: () => void; } @@ -74,13 +80,17 @@ interface LogRowContextGroupHeaderProps { row: LogRowModel; rows: Array; onLoadMoreContext: () => void; + groupPosition: LogGroupPosition; shouldScrollToBottom?: boolean; canLoadMoreRows?: boolean; + logsSortOrder?: LogsSortOrder | null; } interface LogRowContextGroupProps extends LogRowContextGroupHeaderProps { rows: Array; + groupPosition: LogGroupPosition; className?: string; error?: string; + logsSortOrder?: LogsSortOrder | null; } const LogRowContextGroupHeader: React.FunctionComponent = ({ @@ -88,9 +98,22 @@ const LogRowContextGroupHeader: React.FunctionComponent { const { header } = useStyles2(getLogRowContextStyles); + // determine the position in time for this LogGroup by taking the ordering of + // logs and position of the component itself into account. + let logGroupPosition = 'after'; + if (groupPosition === LogGroupPosition.Bottom) { + if (logsSortOrder === LogsSortOrder.Descending) { + logGroupPosition = 'before'; + } + } else if (logsSortOrder === LogsSortOrder.Ascending) { + logGroupPosition = 'before'; + } + return (
- Found {rows.length} rows. + Showing {rows.length} lines {logGroupPosition} match. {(rows.length >= 10 || (rows.length > 10 && rows.length % 10 !== 0)) && canLoadMoreRows && ( { const { commonStyles, logs } = useStyles2(getLogRowContextStyles); const [scrollTop, setScrollTop] = useState(0); @@ -144,6 +169,8 @@ export const LogRowContextGroup: React.FunctionComponent = ({ onLoadMoreContext, hasMoreContextRows, wrapLogMessage, + logsSortOrder, }) => { useEffect(() => { const handleEscKeyDown = (e: KeyboardEvent): void => { @@ -215,6 +243,8 @@ export const LogRowContext: React.FunctionComponent = ({ shouldScrollToBottom canLoadMoreRows={hasMoreContextRows ? hasMoreContextRows.after : false} onLoadMoreContext={onLoadMoreContext} + groupPosition={LogGroupPosition.Top} + logsSortOrder={logsSortOrder} /> )} @@ -226,6 +256,8 @@ export const LogRowContext: React.FunctionComponent = ({ rows={context.before} error={errors && errors.before} className={beforeContext} + groupPosition={LogGroupPosition.Bottom} + logsSortOrder={logsSortOrder} /> )}
diff --git a/public/app/features/logs/components/LogRowContextProvider.tsx b/public/app/features/logs/components/LogRowContextProvider.tsx index 7fa3bbbaa98..12b684813e2 100644 --- a/public/app/features/logs/components/LogRowContextProvider.tsx +++ b/public/app/features/logs/components/LogRowContextProvider.tsx @@ -45,6 +45,7 @@ interface LogRowContextProviderProps { hasMoreContextRows: HasMoreContextRows; updateLimit: () => void; limit: number; + logsSortOrder?: LogsSortOrder | null; }) => JSX.Element; } @@ -208,5 +209,6 @@ export const LogRowContextProvider: React.FunctionComponent setLimit(limit + 10), limit, + logsSortOrder, }); }; diff --git a/public/app/features/logs/components/LogRowMessage.tsx b/public/app/features/logs/components/LogRowMessage.tsx index 9f6f1275c12..f4faff3d214 100644 --- a/public/app/features/logs/components/LogRowMessage.tsx +++ b/public/app/features/logs/components/LogRowMessage.tsx @@ -4,7 +4,7 @@ import React, { PureComponent } from 'react'; import Highlighter from 'react-highlight-words'; import tinycolor from 'tinycolor2'; -import { LogRowModel, findHighlightChunksInText, GrafanaTheme2 } from '@grafana/data'; +import { LogRowModel, findHighlightChunksInText, GrafanaTheme2, LogsSortOrder } from '@grafana/data'; import { withTheme2, Themeable2, IconButton, Tooltip } from '@grafana/ui'; import { LogMessageAnsi } from './LogMessageAnsi'; @@ -28,6 +28,7 @@ interface Props extends Themeable2 { getRows: () => LogRowModel[]; onToggleContext: () => void; updateLimit?: () => void; + logsSortOrder?: LogsSortOrder | null; } const getStyles = (theme: GrafanaTheme2) => { @@ -128,6 +129,7 @@ class UnThemedLogRowMessage extends PureComponent { wrapLogMessage, prettifyLogMessage, onToggleContext, + logsSortOrder, } = this.props; const style = getLogRowStyles(theme, row.logLevel); @@ -150,6 +152,7 @@ class UnThemedLogRowMessage extends PureComponent { wrapLogMessage={wrapLogMessage} hasMoreContextRows={hasMoreContextRows} onOutsideClick={onToggleContext} + logsSortOrder={logsSortOrder} onLoadMoreContext={() => { if (updateLimit) { updateLimit();