From 369dc28ca2cbd8e2f6ce4404a061fb5b6fd1c461 Mon Sep 17 00:00:00 2001 From: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:35:45 -0500 Subject: [PATCH] Logs Table: Add new Controls component to Explore (#103467) * Add logs options to Table --------- Co-authored-by: Matias Chomicki --- public/app/features/explore/Logs/Logs.tsx | 16 +- .../logs/components/ControlledLogRows.tsx | 31 ++- .../logs/components/ControlledLogsTable.tsx | 69 ++++++ .../logs/components/panel/LogListControls.tsx | 223 +++++++++--------- 4 files changed, 223 insertions(+), 116 deletions(-) create mode 100644 public/app/features/logs/components/ControlledLogsTable.tsx diff --git a/public/app/features/explore/Logs/Logs.tsx b/public/app/features/explore/Logs/Logs.tsx index f7e5f4d09e2..22cfb52d6ff 100644 --- a/public/app/features/explore/Logs/Logs.tsx +++ b/public/app/features/explore/Logs/Logs.tsx @@ -213,7 +213,7 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { const [contextOpen, setContextOpen] = useState(false); const [contextRow, setContextRow] = useState(undefined); const [pinLineButtonTooltipTitle, setPinLineButtonTooltipTitle] = useState(PINNED_LOGS_MESSAGE); - const [visualisationType, setVisualisationType] = useState( + const [visualisationType, setVisualisationType] = useState( panelState?.logs?.visualisationType ?? getDefaultVisualisationType() ); const logsContainerRef = useRef(null); @@ -229,7 +229,8 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { const logLevelsRef = useRef(null); const tableHeight = getLogsTableHeight(); - const styles = getStyles(theme, wrapLogMessage, tableHeight); + const setWrapperLineWrapStyles = wrapLogMessage || visualisationType === 'table'; + const styles = getStyles(theme, setWrapperLineWrapStyles, tableHeight); const hasData = logRows && logRows.length > 0; const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...'; @@ -1007,7 +1008,7 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { />
- {visualisationType === 'table' && hasData && ( + {!config.featureToggles.logsPanelControls && visualisationType === 'table' && hasData && (
{/* Width should be full width minus logs navigation and padding */} = (props: Props) => { />
)} - {config.featureToggles.logsPanelControls && visualisationType === 'logs' && hasData && ( + {config.featureToggles.logsPanelControls && hasData && (
{ +export interface ControlledLogRowsProps extends Omit { loading: boolean; logsMeta?: LogsMetaItem[]; loadMoreLogs?: (range: AbsoluteTimeRange) => void; logOptionsStorageKey?: string; onLogOptionsChange?: (option: keyof LogListControlOptions, value: string | boolean | string[]) => void; range: TimeRange; + + /** Props added for Table **/ + visualisationType: LogsVisualisationType; + splitOpen: SplitOpen; + panelState: ExploreLogsPanelState | undefined; + updatePanelState: (panelState: Partial) => void; + datasourceType?: string; + width: number; + logsTableFrames: DataFrame[] | undefined; } -type LogRowsComponentProps = Omit< +export type LogRowsComponentProps = Omit< ControlledLogRowsProps, 'app' | 'dedupStrategy' | 'showLabels' | 'showTime' | 'logsSortOrder' | 'prettifyLogMessage' | 'wrapLogMessage' >; @@ -54,7 +76,8 @@ export const ControlledLogRows = ({ onLogOptionsChange={onLogOptionsChange} wrapLogMessage={wrapLogMessage} > - + {rest.visualisationType === 'logs' && } + {rest.visualisationType === 'table' && } ); }; diff --git a/public/app/features/logs/components/ControlledLogsTable.tsx b/public/app/features/logs/components/ControlledLogsTable.tsx new file mode 100644 index 00000000000..0105e14411d --- /dev/null +++ b/public/app/features/logs/components/ControlledLogsTable.tsx @@ -0,0 +1,69 @@ +import { css } from '@emotion/css'; +import { useMemo } from 'react'; + +import { EventBusSrv, GrafanaTheme2 } from '@grafana/data'; +import { useTheme2 } from '@grafana/ui'; + +import { LogsTableWrap } from '../../explore/Logs/LogsTableWrap'; + +import { LogRowsComponentProps } from './ControlledLogRows'; +import { useLogListContext } from './panel/LogListContext'; +import { LogListControls } from './panel/LogListControls'; + +export const ControlledLogsTable = ({ + loading, + loadMoreLogs, + deduplicatedRows = [], + range, + splitOpen, + onClickFilterLabel, + onClickFilterOutLabel, + panelState, + datasourceType, + updatePanelState, + width, + logsTableFrames, + visualisationType, + ...rest +}: LogRowsComponentProps) => { + const { sortOrder } = useLogListContext(); + const eventBus = useMemo(() => new EventBusSrv(), []); + + const theme = useTheme2(); + const styles = getStyles(theme); + + return ( +
+
+ {/* Width should be full width minus logs navigation and padding */} + +
+ +
+ ); +}; + +const getStyles = (theme: GrafanaTheme2) => { + return { + logRows: css({ + overflowY: 'visible', + width: '100%', + }), + logRowsContainer: css({ + display: 'flex', + }), + }; +}; diff --git a/public/app/features/logs/components/panel/LogListControls.tsx b/public/app/features/logs/components/panel/LogListControls.tsx index 52587d83fe3..2eccad4c4e3 100644 --- a/public/app/features/logs/components/panel/LogListControls.tsx +++ b/public/app/features/logs/components/panel/LogListControls.tsx @@ -2,19 +2,13 @@ import { css } from '@emotion/css'; import { capitalize } from 'lodash'; import { MouseEvent, useCallback, useMemo } from 'react'; -import { - CoreApp, - EventBus, - GrafanaTheme2, - LogLevel, - LogsDedupDescription, - LogsDedupStrategy, - LogsSortOrder, -} from '@grafana/data'; +import { CoreApp, EventBus, LogLevel, LogsDedupDescription, LogsDedupStrategy, LogsSortOrder } from '@grafana/data'; +import { GrafanaTheme2 } from '@grafana/data/'; import { config, reportInteraction } from '@grafana/runtime'; import { Dropdown, IconButton, Menu, useStyles2 } from '@grafana/ui'; import { t } from 'app/core/internationalization'; +import { LogsVisualisationType } from '../../../explore/Logs/Logs'; import { DownloadFormat } from '../../utils'; import { useLogListContext } from './LogListContext'; @@ -22,6 +16,7 @@ import { ScrollToLogsEvent } from './virtualization'; type Props = { eventBus: EventBus; + visualisationType?: LogsVisualisationType; }; const DEDUP_OPTIONS = [ @@ -40,7 +35,7 @@ const FILTER_LEVELS: LogLevel[] = [ LogLevel.critical, ]; -export const LogListControls = ({ eventBus }: Props) => { +export const LogListControls = ({ eventBus, visualisationType = 'logs' }: Props) => { const styles = useStyles2(getStyles); const { app, @@ -204,14 +199,16 @@ export const LogListControls = ({ eventBus }: Props) => { return (
- + {visualisationType === 'logs' && ( + + )} {!inDashboard ? ( <> { } size="lg" /> - - - - - 0 ? styles.controlButtonActive : styles.controlButton} - tooltip={t('logs.logs-controls.display-level', 'Display levels')} - size="lg" - /> - - - {showUniqueLabels !== undefined && ( - - )} - - {prettifyJSON !== undefined && ( - - )} - {syntaxHighlighting !== undefined && ( - + {visualisationType === 'logs' && ( + <> + + + + + 0 ? styles.controlButtonActive : styles.controlButton + } + tooltip={t('logs.logs-controls.display-level', 'Display levels')} + size="lg" + /> + + + {showUniqueLabels !== undefined && ( + + )} + + {prettifyJSON !== undefined && ( + + )} + {syntaxHighlighting !== undefined && ( + + )} + )} {!config.exploreHideLogsDownload && ( <> @@ -332,15 +337,17 @@ export const LogListControls = ({ eventBus }: Props) => { /> )} - + {visualisationType === 'logs' && ( + + )}
); };