mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
feat: show error details in search event patterns (#2065)
## 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:
This commit is contained in:
parent
f8d2edde5a
commit
3ffafced5e
4 changed files with 63 additions and 10 deletions
5
.changeset/tiny-timers-care.md
Normal file
5
.changeset/tiny-timers-care.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
feat: show error details in search event patterns
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
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';
|
||||
|
|
@ -31,14 +34,16 @@ export default function PatternTable({
|
|||
const [selectedPattern, setSelectedPattern] = useState<Pattern | null>(null);
|
||||
|
||||
const {
|
||||
totalCount,
|
||||
error: totalCountError,
|
||||
isLoading: isTotalCountLoading,
|
||||
isTotalCountComplete,
|
||||
totalCount,
|
||||
} = useSearchTotalCount(totalCountConfig, totalCountQueryKeyPrefix);
|
||||
|
||||
const {
|
||||
data: groupedResults,
|
||||
isLoading: isGroupedPatternsLoading,
|
||||
error: groupedPatternsError,
|
||||
patternQueryConfig,
|
||||
} = useGroupedPatterns({
|
||||
config,
|
||||
|
|
@ -54,13 +59,46 @@ export default function PatternTable({
|
|||
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 (
|
||||
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}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export function useSearchTotalCount(
|
|||
data: totalCountData,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
} = useQueriedChartConfig(queriedConfig, {
|
||||
queryKey: [
|
||||
queryKeyPrefix,
|
||||
|
|
@ -78,6 +79,7 @@ export function useSearchTotalCount(
|
|||
totalCount,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
isTotalCountComplete,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,15 +152,20 @@ function usePatterns({
|
|||
limit: { limit: samples },
|
||||
});
|
||||
|
||||
const { data: sampleRows, isLoading: isSampleLoading } =
|
||||
useQueriedChartConfig(
|
||||
configWithPrimaryAndPartitionKey ?? config, // `config` satisfying type, never used due to `enabled` check
|
||||
{ enabled: configWithPrimaryAndPartitionKey != null && enabled },
|
||||
);
|
||||
const {
|
||||
data: sampleRows,
|
||||
isLoading: isSampleLoading,
|
||||
error: sampleError,
|
||||
} = useQueriedChartConfig(
|
||||
configWithPrimaryAndPartitionKey ?? config, // `config` satisfying type, never used due to `enabled` check
|
||||
{ enabled: configWithPrimaryAndPartitionKey != null && enabled },
|
||||
);
|
||||
|
||||
const { data: pyodide, isLoading: isLoadingPyodide } = usePyodide({
|
||||
enabled,
|
||||
});
|
||||
const {
|
||||
data: pyodide,
|
||||
isLoading: isLoadingPyodide,
|
||||
error: pyodideError,
|
||||
} = usePyodide({ enabled });
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['patterns', config],
|
||||
|
|
@ -203,6 +208,7 @@ function usePatterns({
|
|||
|
||||
return {
|
||||
...query,
|
||||
error: sampleError || pyodideError || query.error,
|
||||
isLoading: query.isLoading || isSampleLoading || isLoadingPyodide,
|
||||
patternQueryConfig: configWithPrimaryAndPartitionKey,
|
||||
};
|
||||
|
|
@ -228,6 +234,7 @@ export function useGroupedPatterns({
|
|||
const {
|
||||
data: results,
|
||||
isLoading,
|
||||
error,
|
||||
patternQueryConfig,
|
||||
} = usePatterns({
|
||||
config,
|
||||
|
|
@ -315,6 +322,7 @@ export function useGroupedPatterns({
|
|||
return {
|
||||
data: groupedResults,
|
||||
isLoading,
|
||||
error,
|
||||
miner: results?.miner,
|
||||
sampledRowCount,
|
||||
patternQueryConfig,
|
||||
|
|
|
|||
Loading…
Reference in a new issue