diff --git a/src/Components/ServiceScene/LogsPanelError.tsx b/src/Components/ServiceScene/LogsPanelError.tsx new file mode 100644 index 00000000..bf69d154 --- /dev/null +++ b/src/Components/ServiceScene/LogsPanelError.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { GrotError } from 'Components/GrotError'; +import { Button } from '@grafana/ui'; + +interface Props { + clearFilters(): void; + error: string; +} + +export const LogsPanelError = ({ clearFilters, error }: Props) => { + return ( + + + + No logs found. + + {getMessageFromError(error)} + + Clear filters + + + + ); +}; + +function getMessageFromError(error: string) { + if (error.includes('parse error')) { + return 'Logs could not be retrieved due to invalid filter parameters. Please review your filters and try again.'; + } + + return 'Logs could not be retrieved. Please review your filters or try a different time range.'; +} diff --git a/src/Components/ServiceScene/LogsPanelScene.tsx b/src/Components/ServiceScene/LogsPanelScene.tsx index daafa275..e691c076 100644 --- a/src/Components/ServiceScene/LogsPanelScene.tsx +++ b/src/Components/ServiceScene/LogsPanelScene.tsx @@ -10,8 +10,8 @@ import { SceneQueryRunner, VizPanel, } from '@grafana/scenes'; -import { DataFrame, getValueFormat, LogRowModel } from '@grafana/data'; -import { getLogOption, setDisplayedFields } from '../../services/store'; +import { DataFrame, getValueFormat, LoadingState, LogRowModel, PanelData } from '@grafana/data'; +import { getLogOption, getLogsVolumeOption, setDisplayedFields } from '../../services/store'; import React, { MouseEvent } from 'react'; import { LogsListScene } from './LogsListScene'; import { LoadingPlaceholder, useStyles2 } from '@grafana/ui'; @@ -37,9 +37,13 @@ import { narrowLogsSortOrder } from '../../services/narrowing'; import { logger } from '../../services/logger'; import { LogsSortOrder } from '@grafana/schema'; import { getPrettyQueryExpr } from 'services/scenes'; +import { LogsPanelError } from './LogsPanelError'; +import { clearVariables } from 'services/variableHelpers'; interface LogsPanelSceneState extends SceneObjectState { body?: VizPanel; + error?: string; + logsVolumeCollapsedByError?: boolean; sortOrder?: LogsSortOrder; wrapLogMessage?: boolean; } @@ -53,6 +57,7 @@ export class LogsPanelScene extends SceneObjectBase { super({ sortOrder: getLogsPanelSortOrderFromStore(), wrapLogMessage: Boolean(getLogOption('wrapLogMessage', false)), + error: undefined, ...state, }); @@ -121,6 +126,11 @@ export class LogsPanelScene extends SceneObjectBase { const serviceScene = sceneGraph.getAncestor(this, ServiceScene); this._subs.add( serviceScene.subscribeToState((newState, prevState) => { + if (newState.$data?.state.data?.state === LoadingState.Error) { + this.handleLogsError(newState.$data?.state.data); + } else if (this.state.error) { + this.clearLogsError(); + } if (newState.logsCount !== prevState.logsCount) { if (!this.state.body) { this.setState({ @@ -140,6 +150,27 @@ export class LogsPanelScene extends SceneObjectBase { ); } + handleLogsError(data: PanelData) { + const logsVolumeCollapsedByError = this.state.logsVolumeCollapsedByError ?? !getLogsVolumeOption('collapsed'); + + const error = data.errors?.length ? data.errors[0].message : data.error?.message; + this.setState({ error, logsVolumeCollapsedByError }); + + if (logsVolumeCollapsedByError) { + const logsVolume = sceneGraph.findByKeyAndType(this, logsVolumePanelKey, LogsVolumePanel); + logsVolume.state.panel?.setState({ collapsed: true }); + } + } + + clearLogsError() { + if (this.state.logsVolumeCollapsedByError) { + const logsVolume = sceneGraph.findByKeyAndType(this, logsVolumePanelKey, LogsVolumePanel); + logsVolume.state.panel?.setState({ collapsed: false }); + } + + this.setState({ error: undefined, logsVolumeCollapsedByError: undefined }); + } + onClickShowField = (field: string) => { const parent = this.getParentScene(); const index = parent.state.displayedFields.indexOf(field); @@ -402,12 +433,13 @@ export class LogsPanelScene extends SceneObjectBase { } public static Component = ({ model }: SceneComponentProps) => { - const { body } = model.useState(); + const { body, error } = model.useState(); const styles = useStyles2(getPanelWrapperStyles); if (body) { return ( - + {!error && } + {error && clearVariables(body)} />} ); } diff --git a/src/Components/ServiceScene/LogsVolumePanel.tsx b/src/Components/ServiceScene/LogsVolumePanel.tsx index ccfafebe..edbe39c2 100644 --- a/src/Components/ServiceScene/LogsVolumePanel.tsx +++ b/src/Components/ServiceScene/LogsVolumePanel.tsx @@ -96,7 +96,7 @@ export class LogsVolumePanel extends SceneObjectBase { .setUnit('short') .setMenu(new PanelMenu({ investigationOptions: { labelName: 'level' } })) .setCollapsible(true) - .setCollapsed(Boolean(getLogsVolumeOption('collapsed'))) + .setCollapsed(getLogsVolumeOption('collapsed')) .setHeaderActions(new LogsVolumeActions({})) // 11.5 // .setShowMenuAlways(true) diff --git a/src/services/store.ts b/src/services/store.ts index b379a777..ed8c3732 100644 --- a/src/services/store.ts +++ b/src/services/store.ts @@ -229,7 +229,7 @@ export function setLogsVolumeOption(option: 'collapsed', value: string | undefin } export function getLogsVolumeOption(option: 'collapsed') { - return localStorage.getItem(`${LOGS_VOLUME_LOCALSTORAGE_KEY}.${option}`); + return Boolean(localStorage.getItem(`${LOGS_VOLUME_LOCALSTORAGE_KEY}.${option}`)); } // Log visualization options
+ No logs found. +
{getMessageFromError(error)}