Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logs Panel: Display error message when logs fail to load #1079

Merged
merged 4 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/Components/ServiceScene/LogsPanelError.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<GrotError>
<div>
<p>
<strong>No logs found.</strong>
</p>
<p>{getMessageFromError(error)}</p>
<Button variant="secondary" onClick={clearFilters}>
Clear filters
</Button>
</div>
</GrotError>
);
};

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.';
}
40 changes: 36 additions & 4 deletions src/Components/ServiceScene/LogsPanelScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<Options>;
error?: string;
logsVolumeCollapsedByError?: boolean;
sortOrder?: LogsSortOrder;
wrapLogMessage?: boolean;
}
Expand All @@ -53,6 +57,7 @@ export class LogsPanelScene extends SceneObjectBase<LogsPanelSceneState> {
super({
sortOrder: getLogsPanelSortOrderFromStore(),
wrapLogMessage: Boolean(getLogOption<boolean>('wrapLogMessage', false)),
error: undefined,
...state,
});

Expand Down Expand Up @@ -121,6 +126,11 @@ export class LogsPanelScene extends SceneObjectBase<LogsPanelSceneState> {
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({
Expand All @@ -140,6 +150,27 @@ export class LogsPanelScene extends SceneObjectBase<LogsPanelSceneState> {
);
}

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);
Expand Down Expand Up @@ -402,12 +433,13 @@ export class LogsPanelScene extends SceneObjectBase<LogsPanelSceneState> {
}

public static Component = ({ model }: SceneComponentProps<LogsPanelScene>) => {
const { body } = model.useState();
const { body, error } = model.useState();
const styles = useStyles2(getPanelWrapperStyles);
if (body) {
return (
<span className={styles.panelWrapper}>
<body.Component model={body} />
{!error && <body.Component model={body} />}
{error && <LogsPanelError error={error} clearFilters={() => clearVariables(body)} />}
</span>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/ServiceScene/LogsVolumePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class LogsVolumePanel extends SceneObjectBase<LogsVolumePanelState> {
.setUnit('short')
.setMenu(new PanelMenu({ investigationOptions: { labelName: 'level' } }))
.setCollapsible(true)
.setCollapsed(Boolean(getLogsVolumeOption('collapsed')))
.setCollapsed(getLogsVolumeOption('collapsed'))
.setHeaderActions(new LogsVolumeActions({}))
// 11.5
// .setShowMenuAlways(true)
Expand Down
2 changes: 1 addition & 1 deletion src/services/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading