mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
feat: Improve display of large sizes and volumes of highlighted attributes (#1442)
This commit is contained in:
parent
ff422206c5
commit
238c36fdd2
4 changed files with 99 additions and 47 deletions
5
.changeset/yellow-owls-sin.md
Normal file
5
.changeset/yellow-owls-sin.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
feat: Improve display of large sizes and volumes of highlighted attributes
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
import { useContext, useMemo } from 'react';
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { TSource } from '@hyperdx/common-utils/dist/types';
|
||||
import { Flex } from '@mantine/core';
|
||||
import { Anchor, Flex } from '@mantine/core';
|
||||
|
||||
import { RowSidePanelContext } from './DBRowSidePanel';
|
||||
import EventTag from './EventTag';
|
||||
|
||||
const DEFAULT_ATTRIBUTES_TO_SHOW = 12;
|
||||
|
||||
export type HighlightedAttribute = {
|
||||
source: TSource;
|
||||
displayedKey: string;
|
||||
|
|
@ -18,6 +20,8 @@ export function DBHighlightedAttributesList({
|
|||
}: {
|
||||
attributes: HighlightedAttribute[];
|
||||
}) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const {
|
||||
onPropertyAddClick,
|
||||
generateSearchUrl,
|
||||
|
|
@ -25,15 +29,19 @@ export function DBHighlightedAttributesList({
|
|||
} = useContext(RowSidePanelContext);
|
||||
|
||||
const sortedAttributes = useMemo(() => {
|
||||
return attributes.sort(
|
||||
(a, b) =>
|
||||
a.displayedKey.localeCompare(b.displayedKey) ||
|
||||
a.value.localeCompare(b.value),
|
||||
);
|
||||
}, [attributes]);
|
||||
return attributes
|
||||
.sort(
|
||||
(a, b) =>
|
||||
a.displayedKey.localeCompare(b.displayedKey) ||
|
||||
a.value.localeCompare(b.value),
|
||||
)
|
||||
.slice(0, isExpanded ? attributes.length : DEFAULT_ATTRIBUTES_TO_SHOW);
|
||||
}, [attributes, isExpanded]);
|
||||
|
||||
const hiddenAttributesCount = attributes.length - sortedAttributes.length;
|
||||
|
||||
return (
|
||||
<Flex wrap="wrap" gap="2px" mb="md">
|
||||
<Flex wrap="wrap" gap="2px" mb="md" align="baseline">
|
||||
{sortedAttributes.map(({ displayedKey, value, sql, lucene, source }) => (
|
||||
<EventTag
|
||||
displayedKey={displayedKey}
|
||||
|
|
@ -62,6 +70,11 @@ export function DBHighlightedAttributesList({
|
|||
}
|
||||
/>
|
||||
))}
|
||||
{attributes.length > DEFAULT_ATTRIBUTES_TO_SHOW && (
|
||||
<Anchor size="xs" onClick={() => setIsExpanded(!isExpanded)}>
|
||||
{isExpanded ? 'Show Less' : `Show ${hiddenAttributesCount} More...`}
|
||||
</Anchor>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
SourceKind,
|
||||
TSource,
|
||||
} from '@hyperdx/common-utils/dist/types';
|
||||
import { Anchor, Box, Divider, Group, Text } from '@mantine/core';
|
||||
import { Anchor, Box, Code, Divider, Group, Text } from '@mantine/core';
|
||||
|
||||
import { ContactSupportText } from '@/components/ContactSupportText';
|
||||
import useOffsetPaginatedQuery from '@/hooks/useOffsetPaginatedQuery';
|
||||
|
|
@ -245,28 +245,37 @@ export function useEventsAroundFocus({
|
|||
/** A lucene expression that identifies rows to be hidden. Hidden rows will be returned with a `__hdx_hidden: true` column. */
|
||||
hiddenRowExpression?: string;
|
||||
}) {
|
||||
let isFetching = false;
|
||||
const { config, alias, type } = useMemo(
|
||||
() => getConfig(tableSource, traceId, hiddenRowExpression),
|
||||
[tableSource, traceId, hiddenRowExpression],
|
||||
);
|
||||
|
||||
const { data: beforeSpanData, isFetching: isBeforeSpanFetching } =
|
||||
useEventsData({
|
||||
config,
|
||||
dateRangeStartInclusive: true,
|
||||
dateRange: [dateRange[0], focusDate],
|
||||
enabled,
|
||||
});
|
||||
const { data: afterSpanData, isFetching: isAfterSpanFetching } =
|
||||
useEventsData({
|
||||
config,
|
||||
dateRangeStartInclusive: false,
|
||||
dateRange: [focusDate, dateRange[1]],
|
||||
enabled,
|
||||
});
|
||||
isFetching = isFetching || isBeforeSpanFetching || isAfterSpanFetching;
|
||||
const {
|
||||
data: beforeSpanData,
|
||||
isFetching: isBeforeSpanFetching,
|
||||
error: beforeSpanError,
|
||||
} = useEventsData({
|
||||
config,
|
||||
dateRangeStartInclusive: true,
|
||||
dateRange: [dateRange[0], focusDate],
|
||||
enabled,
|
||||
});
|
||||
|
||||
const {
|
||||
data: afterSpanData,
|
||||
isFetching: isAfterSpanFetching,
|
||||
error: afterSpanError,
|
||||
} = useEventsData({
|
||||
config,
|
||||
dateRangeStartInclusive: false,
|
||||
dateRange: [focusDate, dateRange[1]],
|
||||
enabled,
|
||||
});
|
||||
|
||||
const isFetching = isBeforeSpanFetching || isAfterSpanFetching;
|
||||
const meta = beforeSpanData?.meta ?? afterSpanData?.meta;
|
||||
const error = beforeSpanError || afterSpanError;
|
||||
|
||||
const rowWhere = useRowWhere({ meta, aliasMap: alias });
|
||||
const rows = useMemo(() => {
|
||||
// Sometimes meta has not loaded yet
|
||||
|
|
@ -294,6 +303,7 @@ export function useEventsAroundFocus({
|
|||
rows,
|
||||
meta,
|
||||
isFetching,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -352,6 +362,7 @@ export function DBTraceWaterfallChartContainer({
|
|||
rows: traceRowsData,
|
||||
isFetching: traceIsFetching,
|
||||
meta: traceRowsMeta,
|
||||
error: traceError,
|
||||
} = useEventsAroundFocus({
|
||||
tableSource: traceTableSource,
|
||||
focusDate,
|
||||
|
|
@ -364,6 +375,7 @@ export function DBTraceWaterfallChartContainer({
|
|||
rows: logRowsData,
|
||||
isFetching: logIsFetching,
|
||||
meta: logRowsMeta,
|
||||
error: logError,
|
||||
} = useEventsAroundFocus({
|
||||
// search data if logTableModel exist
|
||||
// search invalid date range if no logTableModel(react hook need execute no matter what)
|
||||
|
|
@ -376,6 +388,8 @@ export function DBTraceWaterfallChartContainer({
|
|||
});
|
||||
|
||||
const isFetching = traceIsFetching || logIsFetching;
|
||||
const error = traceError || logError;
|
||||
|
||||
const rows: any[] = useMemo(
|
||||
() => [...traceRowsData, ...logRowsData],
|
||||
[traceRowsData, logRowsData],
|
||||
|
|
@ -799,6 +813,9 @@ export function DBTraceWaterfallChartContainer({
|
|||
)}
|
||||
</span>
|
||||
</Group>
|
||||
{!isFetching && !error && (
|
||||
<DBHighlightedAttributesList attributes={highlightedAttributeValues} />
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
|
|
@ -808,6 +825,20 @@ export function DBTraceWaterfallChartContainer({
|
|||
>
|
||||
{isFetching ? (
|
||||
<div className="my-3">Loading Traces...</div>
|
||||
) : error ? (
|
||||
<Box mt="lg">
|
||||
<Text my="sm" size="sm">
|
||||
An error occurred while fetching trace data:
|
||||
</Text>
|
||||
<Code
|
||||
block
|
||||
style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
>
|
||||
{error.message}
|
||||
</Code>
|
||||
</Box>
|
||||
) : rows == null ? (
|
||||
<div>
|
||||
An unknown error occurred. <ContactSupportText />
|
||||
|
|
@ -816,9 +847,6 @@ export function DBTraceWaterfallChartContainer({
|
|||
<div className="my-3">No matching spans or logs found</div>
|
||||
) : (
|
||||
<>
|
||||
<DBHighlightedAttributesList
|
||||
attributes={highlightedAttributeValues}
|
||||
/>
|
||||
<TimelineChart
|
||||
style={{
|
||||
overflowY: 'auto',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useState } from 'react';
|
|||
import Link from 'next/link';
|
||||
import SqlString from 'sqlstring';
|
||||
import { SearchConditionLanguage } from '@hyperdx/common-utils/dist/types';
|
||||
import { Button, Popover, Stack, Tooltip } from '@mantine/core';
|
||||
import { Button, Group, Popover, Stack, Text, Tooltip } from '@mantine/core';
|
||||
import { IconLink } from '@tabler/icons-react';
|
||||
|
||||
import { isLinkableUrl } from '@/utils/highlightedAttributes';
|
||||
|
|
@ -62,14 +62,15 @@ export default function EventTag({
|
|||
onChange={setOpened}
|
||||
>
|
||||
<Popover.Target>
|
||||
{isLink ? (
|
||||
<Tooltip
|
||||
label={value}
|
||||
withArrow
|
||||
maw={400}
|
||||
multiline
|
||||
style={{ wordBreak: 'break-word' }}
|
||||
>
|
||||
<Tooltip
|
||||
label={value}
|
||||
withArrow
|
||||
maw={400}
|
||||
multiline
|
||||
style={{ wordBreak: 'break-word' }}
|
||||
position="bottom"
|
||||
>
|
||||
{isLink ? (
|
||||
<a
|
||||
href={encodeURI(value)}
|
||||
target="_blank"
|
||||
|
|
@ -79,15 +80,20 @@ export default function EventTag({
|
|||
{displayedKey || name}
|
||||
<IconLink size={14} className="ms-1" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<div
|
||||
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
|
||||
onClick={() => setOpened(!opened)}
|
||||
>
|
||||
{displayedKey || name}: {value}
|
||||
</div>
|
||||
)}
|
||||
) : (
|
||||
<Group
|
||||
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
|
||||
wrap="nowrap"
|
||||
gap={0}
|
||||
onClick={() => setOpened(!opened)}
|
||||
>
|
||||
{displayedKey || name}:
|
||||
<Text size="xs" ms={4} maw={300} truncate>
|
||||
{value}
|
||||
</Text>
|
||||
</Group>
|
||||
)}
|
||||
</Tooltip>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown p={2}>
|
||||
<Stack gap={0} justify="stretch">
|
||||
|
|
|
|||
Loading…
Reference in a new issue