mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
logs-volume: remove custom timeout (#45041)
* logs-volume: remove custom timeout * make error message a little better
This commit is contained in:
@@ -39,7 +39,7 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { getThemeColor } from 'app/core/utils/colors';
|
||||
import { SIPrefix } from '@grafana/data/src/valueFormats/symbolFormatters';
|
||||
import { Observable, throwError, timeout } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export const LIMIT_LABEL = 'Line limit';
|
||||
export const COMMON_LABELS = 'Common labels';
|
||||
@@ -617,10 +617,7 @@ function aggregateFields(dataFrames: DataFrame[], config: FieldConfig): DataFram
|
||||
return aggregatedDataFrame;
|
||||
}
|
||||
|
||||
const LOGS_VOLUME_QUERY_DEFAULT_TIMEOUT = 60000;
|
||||
|
||||
type LogsVolumeQueryOptions<T extends DataQuery> = {
|
||||
timeout?: number;
|
||||
extractLevel: (dataFrame: DataFrame) => LogLevel;
|
||||
targets: T[];
|
||||
range: TimeRange;
|
||||
@@ -651,45 +648,36 @@ export function queryLogsVolume<T extends DataQuery>(
|
||||
data: [],
|
||||
});
|
||||
|
||||
const subscription = (datasource.query(logsVolumeRequest) as Observable<DataQueryResponse>)
|
||||
.pipe(
|
||||
timeout({
|
||||
each: options.timeout || LOGS_VOLUME_QUERY_DEFAULT_TIMEOUT,
|
||||
with: () => throwError(new Error('Request timed-out. Please make your query more specific and try again.')),
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
const aggregatedLogsVolume = aggregateRawLogsVolume(rawLogsVolume, options.extractLevel);
|
||||
if (aggregatedLogsVolume[0]) {
|
||||
aggregatedLogsVolume[0].meta = {
|
||||
custom: {
|
||||
targets: options.targets,
|
||||
absoluteRange: { from: options.range.from.valueOf(), to: options.range.to.valueOf() },
|
||||
},
|
||||
};
|
||||
}
|
||||
observer.next({
|
||||
state: LoadingState.Done,
|
||||
error: undefined,
|
||||
data: aggregatedLogsVolume,
|
||||
});
|
||||
observer.complete();
|
||||
},
|
||||
next: (dataQueryResponse: DataQueryResponse) => {
|
||||
rawLogsVolume = rawLogsVolume.concat(dataQueryResponse.data.map(toDataFrame));
|
||||
},
|
||||
error: (error) => {
|
||||
const errorMessage = error.data?.message || error.statusText || error.message;
|
||||
console.error('Log volume query failed with error: ', errorMessage);
|
||||
observer.next({
|
||||
state: LoadingState.Error,
|
||||
error: error,
|
||||
data: [],
|
||||
});
|
||||
observer.error(error);
|
||||
},
|
||||
});
|
||||
const subscription = (datasource.query(logsVolumeRequest) as Observable<DataQueryResponse>).subscribe({
|
||||
complete: () => {
|
||||
const aggregatedLogsVolume = aggregateRawLogsVolume(rawLogsVolume, options.extractLevel);
|
||||
if (aggregatedLogsVolume[0]) {
|
||||
aggregatedLogsVolume[0].meta = {
|
||||
custom: {
|
||||
targets: options.targets,
|
||||
absoluteRange: { from: options.range.from.valueOf(), to: options.range.to.valueOf() },
|
||||
},
|
||||
};
|
||||
}
|
||||
observer.next({
|
||||
state: LoadingState.Done,
|
||||
error: undefined,
|
||||
data: aggregatedLogsVolume,
|
||||
});
|
||||
observer.complete();
|
||||
},
|
||||
next: (dataQueryResponse: DataQueryResponse) => {
|
||||
rawLogsVolume = rawLogsVolume.concat(dataQueryResponse.data.map(toDataFrame));
|
||||
},
|
||||
error: (error) => {
|
||||
observer.next({
|
||||
state: LoadingState.Error,
|
||||
error: error,
|
||||
data: [],
|
||||
});
|
||||
observer.error(error);
|
||||
},
|
||||
});
|
||||
return () => {
|
||||
subscription?.unsubscribe();
|
||||
};
|
||||
|
||||
@@ -40,11 +40,23 @@ describe('LogsVolumePanel', () => {
|
||||
expect(screen.getByText('ExploreGraph')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows warning message without details', () => {
|
||||
it('shows short warning message', () => {
|
||||
renderPanel({ state: LoadingState.Error, error: { data: { message: 'Test error message' } }, data: [] });
|
||||
expect(screen.getByText('Failed to load log volume for this query')).toBeInTheDocument();
|
||||
expect(screen.getByText('Please check console logs for more details.')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Test error message')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Test error message')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows long warning message', () => {
|
||||
// we make a long message
|
||||
const messagePart = 'One two three four five six seven eight nine ten.';
|
||||
const message = messagePart + ' ' + messagePart + ' ' + messagePart;
|
||||
|
||||
renderPanel({ state: LoadingState.Error, error: { data: { message } }, data: [] });
|
||||
expect(screen.getByText('Failed to load log volume for this query')).toBeInTheDocument();
|
||||
expect(screen.queryByText(message)).not.toBeInTheDocument();
|
||||
const button = screen.getByText('Show details');
|
||||
button.click();
|
||||
expect(screen.getByText(message)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show the panel when there is no volume data', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AbsoluteTimeRange, DataQueryResponse, LoadingState, SplitOpen, TimeZone } from '@grafana/data';
|
||||
import { AbsoluteTimeRange, DataQueryError, DataQueryResponse, LoadingState, SplitOpen, TimeZone } from '@grafana/data';
|
||||
import { Alert, Button, Collapse, InlineField, TooltipDisplayMode, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { ExploreGraph } from './ExploreGraph';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
type Props = {
|
||||
@@ -14,6 +14,35 @@ type Props = {
|
||||
onLoadLogsVolume: () => void;
|
||||
};
|
||||
|
||||
const SHORT_ERROR_MESSAGE_LIMIT = 100;
|
||||
|
||||
function ErrorAlert(props: { error: DataQueryError }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
// generic get-error-message-logic, taken from
|
||||
// /public/app/features/explore/ErrorContainer.tsx
|
||||
const message = props.error.message || props.error.data?.message || '';
|
||||
|
||||
const showButton = !isOpen && message.length > SHORT_ERROR_MESSAGE_LIMIT;
|
||||
|
||||
return (
|
||||
<Alert title="Failed to load log volume for this query" severity="warning">
|
||||
{showButton ? (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
Show details
|
||||
</Button>
|
||||
) : (
|
||||
message
|
||||
)}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export function LogsVolumePanel(props: Props) {
|
||||
const { width, logsVolumeData, absoluteRange, timeZone, splitOpen, onUpdateTimeRange, onLoadLogsVolume } = props;
|
||||
const theme = useTheme2();
|
||||
@@ -26,11 +55,7 @@ export function LogsVolumePanel(props: Props) {
|
||||
if (!logsVolumeData) {
|
||||
return null;
|
||||
} else if (logsVolumeData?.error) {
|
||||
return (
|
||||
<Alert title="Failed to load log volume for this query" severity="warning">
|
||||
Please check console logs for more details.
|
||||
</Alert>
|
||||
);
|
||||
return <ErrorAlert error={logsVolumeData?.error} />;
|
||||
} else if (logsVolumeData?.state === LoadingState.Loading) {
|
||||
LogsVolumePanelContent = <span>Log volume is loading...</span>;
|
||||
} else if (logsVolumeData?.data) {
|
||||
|
||||
@@ -70,12 +70,6 @@ export const DEFAULT_MAX_LINES = 1000;
|
||||
export const LOKI_ENDPOINT = '/loki/api/v1';
|
||||
const NS_IN_MS = 1000000;
|
||||
|
||||
/**
|
||||
* Loki's logs volume query may be expensive as it requires counting all logs in the selected range. If such query
|
||||
* takes too much time it may need be made more specific to limit number of logs processed under the hood.
|
||||
*/
|
||||
const LOGS_VOLUME_TIMEOUT = 10000;
|
||||
|
||||
const RANGE_QUERY_ENDPOINT = `${LOKI_ENDPOINT}/query_range`;
|
||||
const INSTANT_QUERY_ENDPOINT = `${LOKI_ENDPOINT}/query`;
|
||||
|
||||
@@ -150,7 +144,6 @@ export class LokiDatasource
|
||||
});
|
||||
|
||||
return queryLogsVolume(this, logsVolumeRequest, {
|
||||
timeout: LOGS_VOLUME_TIMEOUT,
|
||||
extractLevel,
|
||||
range: request.range,
|
||||
targets: request.targets,
|
||||
|
||||
Reference in New Issue
Block a user