mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
## Summary - Shows query errors in search page event patterns in the same way as for event deltas. - Previously, a loading state was shown indefinitely if there was an error. ### Screenshots or video <img width="1375" height="554" alt="Screenshot 2026-04-07 at 16 14 37" src="https://github.com/user-attachments/assets/25417f1a-bfd3-44ca-bcd6-aa24156fad14" /> ### How to test locally or on Vercel 1. Easiest to test locally by manually throwing from the `useQueriedChartConfig` query function. ### References - Linear Issue: Closes HDX-3933 - Related PRs:
140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
import { useMemo, useState } from 'react';
|
|
import { ClickHouseQueryError } from '@hyperdx/common-utils/dist/clickhouse';
|
|
import {
|
|
BuilderChartConfigWithDateRange,
|
|
SourceKind,
|
|
TSource,
|
|
} from '@hyperdx/common-utils/dist/types';
|
|
import { Box, Code, Container, Text } from '@mantine/core';
|
|
|
|
import { SQLPreview } from '@/components/ChartSQLPreview';
|
|
import { RawLogTable } from '@/components/DBRowTable';
|
|
import { useSearchTotalCount } from '@/components/SearchTotalCountChart';
|
|
import { Pattern, useGroupedPatterns } from '@/hooks/usePatterns';
|
|
|
|
import PatternSidePanel from './PatternSidePanel';
|
|
|
|
const emptyMap = new Map();
|
|
|
|
export default function PatternTable({
|
|
config,
|
|
totalCountConfig,
|
|
totalCountQueryKeyPrefix,
|
|
bodyValueExpression,
|
|
source,
|
|
}: {
|
|
config: BuilderChartConfigWithDateRange;
|
|
totalCountConfig: BuilderChartConfigWithDateRange;
|
|
bodyValueExpression: string;
|
|
totalCountQueryKeyPrefix: string;
|
|
source?: TSource;
|
|
}) {
|
|
const SAMPLES = 10_000;
|
|
|
|
const [selectedPattern, setSelectedPattern] = useState<Pattern | null>(null);
|
|
|
|
const {
|
|
error: totalCountError,
|
|
isLoading: isTotalCountLoading,
|
|
isTotalCountComplete,
|
|
totalCount,
|
|
} = useSearchTotalCount(totalCountConfig, totalCountQueryKeyPrefix);
|
|
|
|
const {
|
|
data: groupedResults,
|
|
isLoading: isGroupedPatternsLoading,
|
|
error: groupedPatternsError,
|
|
patternQueryConfig,
|
|
} = useGroupedPatterns({
|
|
config,
|
|
samples: SAMPLES,
|
|
bodyValueExpression,
|
|
severityTextExpression:
|
|
(source?.kind === SourceKind.Log && source.severityTextExpression) || '',
|
|
statusCodeExpression:
|
|
(source?.kind === SourceKind.Trace && source.statusCodeExpression) || '',
|
|
totalCount,
|
|
});
|
|
|
|
const isLoading =
|
|
isTotalCountLoading || !isTotalCountComplete || isGroupedPatternsLoading;
|
|
|
|
const error = totalCountError || groupedPatternsError;
|
|
|
|
const sortedGroupedResults = useMemo(() => {
|
|
return Object.values(groupedResults).sort(
|
|
(a, b) => b.count - a.count,
|
|
) as Pattern[];
|
|
}, [groupedResults]);
|
|
|
|
return error ? (
|
|
<Container style={{ overflow: 'auto' }}>
|
|
<Box mt="lg">
|
|
<Text my="sm" size="sm">
|
|
Error Message:
|
|
</Text>
|
|
<Code
|
|
block
|
|
style={{
|
|
whiteSpace: 'pre-wrap',
|
|
}}
|
|
>
|
|
{error.message}
|
|
</Code>
|
|
</Box>
|
|
{error instanceof ClickHouseQueryError && (
|
|
<Box mt="lg">
|
|
<Text my="sm" size="sm">
|
|
Original Query:
|
|
</Text>
|
|
<Code
|
|
block
|
|
style={{
|
|
whiteSpace: 'pre-wrap',
|
|
}}
|
|
>
|
|
<SQLPreview data={error.query} formatData />
|
|
</Code>
|
|
</Box>
|
|
)}
|
|
</Container>
|
|
) : (
|
|
<>
|
|
<RawLogTable
|
|
isLive={false}
|
|
wrapLines={true}
|
|
isLoading={isLoading}
|
|
rows={sortedGroupedResults ?? []}
|
|
displayedColumns={[
|
|
'__hdx_pattern_trend',
|
|
'countStr',
|
|
'severityText',
|
|
'pattern',
|
|
]}
|
|
onRowDetailsClick={row => setSelectedPattern(row as Pattern)}
|
|
hasNextPage={false}
|
|
fetchNextPage={() => {}}
|
|
highlightedLineId={''}
|
|
columnTypeMap={emptyMap}
|
|
generateRowId={row => ({ where: row.id, aliasWith: [] })}
|
|
columnNameMap={{
|
|
__hdx_pattern_trend: 'Trend',
|
|
countStr: 'Count',
|
|
pattern: 'Pattern',
|
|
severityText: 'Level',
|
|
}}
|
|
config={patternQueryConfig}
|
|
showExpandButton={false}
|
|
/>
|
|
{selectedPattern && source && (
|
|
<PatternSidePanel
|
|
isOpen
|
|
source={source}
|
|
pattern={selectedPattern}
|
|
bodyValueExpression={bodyValueExpression}
|
|
onClose={() => setSelectedPattern(null)}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|