chore: metrics explorer fixes (#7362)

This commit is contained in:
Amlan Kumar Nandy 2025-03-19 17:31:36 +05:30 committed by GitHub
parent 097e4ca948
commit 3d3dd98549
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 145 additions and 79 deletions

View File

@ -12,12 +12,22 @@ export interface MetricsListFilterKeysResponse {
};
}
export interface GetMetricsListFilterKeysParams {
searchText: string;
limit?: number;
}
export const getMetricsListFilterKeys = async (
params: GetMetricsListFilterKeysParams,
signal?: AbortSignal,
headers?: Record<string, string>,
): Promise<SuccessResponse<MetricsListFilterKeysResponse> | ErrorResponse> => {
try {
const response = await axios.get('/metrics/filters/keys', {
params: {
searchText: params.searchText,
limit: params.limit,
},
signal,
headers,
});

View File

@ -0,0 +1,16 @@
import { Typography } from 'antd';
import { Empty } from 'antd/lib';
export default function EmptyMetricsSearch(): JSX.Element {
return (
<div className="empty-metrics-search">
<Empty
description={
<Typography.Title level={5}>
Please build and run query to see the result
</Typography.Title>
}
/>
</div>
);
}

View File

@ -22,7 +22,11 @@
.query-section {
max-height: 450px;
overflow-y: scroll;
overflow-y: auto;
.rc-virtual-list-holder {
height: 150px;
}
}
.explore-tabs {
@ -51,17 +55,31 @@
}
.explore-content {
.ant-space {
margin-top: 10px;
margin-bottom: 20px;
}
.empty-metrics-search {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.time-series-container {
display: flex;
gap: 10px;
width: 100%;
height: fit-content;
overflow-y: scroll;
}
.time-series-view {
min-width: 100%;
width: 100%;
}
.time-series-view {
min-width: 100%;
width: 100%;
}
.related-metrics-container {

View File

@ -23,7 +23,6 @@ import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToD
import { v4 as uuid } from 'uuid';
import QuerySection from './QuerySection';
import RelatedMetrics from './RelatedMetrics';
import TimeSeries from './TimeSeries';
import { ExplorerTabs } from './types';
@ -51,15 +50,6 @@ function Explorer(): JSX.Element {
const handleToggleShowOneChartPerQuery = (): void =>
toggleShowOneChartPerQuery(!showOneChartPerQuery);
const metricNames = useMemo(() => {
if (!stagedQuery || stagedQuery.builder.queryData.length === 0) {
return [];
}
return stagedQuery.builder.queryData.map(
(query) => query.aggregateAttribute.key,
);
}, [stagedQuery]);
const exportDefaultQuery = useMemo(
() =>
updateAllQueriesOperators(
@ -159,7 +149,8 @@ function Explorer(): JSX.Element {
>
<Typography.Text>Time series</Typography.Text>
</Button>
<Button
{/* TODO: Enable once we have resolved all related metrics issues */}
{/* <Button
value={ExplorerTabs.RELATED_METRICS}
className={classNames('tab', {
'selected-view': selectedTab === ExplorerTabs.RELATED_METRICS,
@ -167,15 +158,16 @@ function Explorer(): JSX.Element {
onClick={(): void => setSelectedTab(ExplorerTabs.RELATED_METRICS)}
>
<Typography.Text>Related</Typography.Text>
</Button>
</Button> */}
</Button.Group>
<div className="explore-content">
{selectedTab === ExplorerTabs.TIME_SERIES && (
<TimeSeries showOneChartPerQuery={showOneChartPerQuery} />
)}
{selectedTab === ExplorerTabs.RELATED_METRICS && (
{/* TODO: Enable once we have resolved all related metrics issues */}
{/* {selectedTab === ExplorerTabs.RELATED_METRICS && (
<RelatedMetrics metricNames={metricNames} />
)}
)} */}
</div>
</div>
<ExplorerOptionWrapper

View File

@ -2,11 +2,12 @@ import classNames from 'classnames';
import { ENTITY_VERSION_V4 } from 'constants/app';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters/BuilderUnitsFilter/BuilderUnits';
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
import { convertDataValueToMs } from 'container/TimeSeriesView/utils';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import { useQueries } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
@ -55,6 +56,8 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
[showOneChartPerQuery, stagedQuery],
);
const [yAxisUnit, setYAxisUnit] = useState<string>('');
const queries = useQueries(
queryPayloads.map((payload, index) => ({
queryKey: [
@ -98,12 +101,17 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
[showOneChartPerQuery, queries],
);
const onUnitChangeHandler = (value: string): void => {
setYAxisUnit(value);
};
return (
<div
className={classNames({
'time-series-container': changeLayoutForOneChartPerQuery,
})}
>
<BuilderUnitsFilter onChange={onUnitChangeHandler} yAxisUnit={yAxisUnit} />
{responseData.map((datapoint, index) => (
<div
className="time-series-view"
@ -115,7 +123,7 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
isError={queries[index].isError}
isLoading={queries[index].isLoading}
data={datapoint}
yAxisUnit={isValidToConvertToMs ? 'ms' : 'short'}
yAxisUnit={yAxisUnit}
dataSource={DataSource.METRICS}
/>
</div>

View File

@ -1,36 +1,30 @@
import { Button, Collapse, Input, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { ResizeTable } from 'components/ResizeTable';
import ROUTES from 'constants/routes';
import { DataType } from 'container/LogDetailedView/TableView';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { Search } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import { AllAttributesProps } from './types';
import { getMetricDetailsQuery } from './utils';
function AllAttributes({
attributes,
metricName,
}: AllAttributesProps): JSX.Element {
function AllAttributes({ attributes }: AllAttributesProps): JSX.Element {
const [searchString, setSearchString] = useState('');
const [activeKey, setActiveKey] = useState<string | string[]>(
'all-attributes',
);
const { safeNavigate } = useSafeNavigate();
// const { safeNavigate } = useSafeNavigate();
const goToMetricsExploreWithAppliedAttribute = useCallback(
(key: string, value: string) => {
const compositeQuery = getMetricDetailsQuery(metricName, { key, value });
const encodedCompositeQuery = JSON.stringify(compositeQuery);
safeNavigate(
`${ROUTES.METRICS_EXPLORER_EXPLORER}?compositeQuery=${encodedCompositeQuery}`,
);
},
[metricName, safeNavigate],
);
// const goToMetricsExploreWithAppliedAttribute = useCallback(
// (key: string, value: string) => {
// const compositeQuery = getMetricDetailsQuery(metricName, { key, value });
// const encodedCompositeQuery = JSON.stringify(compositeQuery);
// safeNavigate(
// `${ROUTES.METRICS_EXPLORER_EXPLORER}?compositeQuery=${encodedCompositeQuery}`,
// );
// },
// [metricName, safeNavigate],
// );
const filteredAttributes = useMemo(
() =>
@ -87,9 +81,10 @@ function AllAttributes({
<Button
key={attribute}
type="text"
onClick={(): void => {
goToMetricsExploreWithAppliedAttribute(field.key, attribute);
}}
// TODO: Enable this once we have fixed the redirect issue
// onClick={(): void => {
// goToMetricsExploreWithAppliedAttribute(field.key, attribute);
// }}
>
<Typography.Text>{attribute}</Typography.Text>
</Button>
@ -98,7 +93,7 @@ function AllAttributes({
),
},
],
[goToMetricsExploreWithAppliedAttribute],
[],
);
const items = useMemo(

View File

@ -2,21 +2,11 @@ import './MetricDetails.styles.scss';
import '../Summary/Summary.styles.scss';
import { Color } from '@signozhq/design-tokens';
import {
Button,
Divider,
Drawer,
Empty,
Skeleton,
Tooltip,
Typography,
} from 'antd';
import ROUTES from 'constants/routes';
import { Divider, Drawer, Empty, Skeleton, Tooltip, Typography } from 'antd';
import { useGetMetricDetails } from 'hooks/metricsExplorer/useGetMetricDetails';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { Compass, X } from 'lucide-react';
import { useCallback, useMemo } from 'react';
import { X } from 'lucide-react';
import { useMemo } from 'react';
import { formatNumberIntoHumanReadableFormat } from '../Summary/utils';
import AllAttributes from './AllAttributes';
@ -26,7 +16,6 @@ import { MetricDetailsProps } from './types';
import {
formatNumberToCompactFormat,
formatTimestampToReadableDate,
getMetricDetailsQuery,
} from './utils';
function MetricDetails({
@ -35,7 +24,7 @@ function MetricDetails({
metricName,
}: MetricDetailsProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const { safeNavigate } = useSafeNavigate();
// const { safeNavigate } = useSafeNavigate();
const {
data,
@ -72,15 +61,15 @@ function MetricDetails({
);
}, [metric]);
const goToMetricsExplorerwithSelectedMetric = useCallback(() => {
if (metricName) {
const compositeQuery = getMetricDetailsQuery(metricName);
const encodedCompositeQuery = JSON.stringify(compositeQuery);
safeNavigate(
`${ROUTES.METRICS_EXPLORER_EXPLORER}?compositeQuery=${encodedCompositeQuery}`,
);
}
}, [metricName, safeNavigate]);
// const goToMetricsExplorerwithSelectedMetric = useCallback(() => {
// if (metricName) {
// const compositeQuery = getMetricDetailsQuery(metricName);
// const encodedCompositeQuery = JSON.stringify(compositeQuery);
// safeNavigate(
// `${ROUTES.METRICS_EXPLORER_EXPLORER}?compositeQuery=${encodedCompositeQuery}`,
// );
// }
// }, [metricName, safeNavigate]);
const isMetricDetailsError = metricDetailsError || !metric;
@ -93,7 +82,8 @@ function MetricDetails({
<Divider type="vertical" />
<Typography.Text>{metric?.name}</Typography.Text>
</div>
<Button
{/* TODO: Enable this once we have fixed the redirect issue */}
{/* <Button
onClick={goToMetricsExplorerwithSelectedMetric}
icon={<Compass size={16} />}
disabled={!metricName}
@ -101,7 +91,7 @@ function MetricDetails({
rel="noopener noreferrer"
>
Open in Explorer
</Button>
</Button> */}
</div>
}
placement="right"

View File

@ -16,6 +16,7 @@ import { metricsTableColumns } from './utils';
function MetricsTable({
isLoading,
isError,
data,
pageSize,
currentPage,
@ -76,7 +77,9 @@ function MetricsTable({
className="empty-state-svg"
/>
<Typography.Text className="no-metrics-message">
This query had no results. Edit your query and try again!
{isError
? 'Error fetching metrics. If the problem persists, please contact support.'
: 'This query had no results. Edit your query and try again!'}
</Typography.Text>
</div>
),

View File

@ -22,6 +22,7 @@ function MetricsTreemap({
viewType,
data,
isLoading,
isError,
openMetricDetails,
}: MetricsTreemapProps): JSX.Element {
const { width: windowWidth } = useWindowSize();
@ -60,7 +61,6 @@ function MetricsTreemap({
if (
!data ||
!data.data ||
data?.status === 'error' ||
(data?.status === 'success' && !data?.data?.[viewType])
) {
return (
@ -71,6 +71,15 @@ function MetricsTreemap({
);
}
if (data?.status === 'error' || isError) {
return (
<Empty
description="Error fetching metrics. If the problem persists, please contact support."
style={{ width: treemapWidth, height: TREEMAP_HEIGHT, paddingTop: 30 }}
/>
);
}
return (
<div className="metrics-treemap-container">
<div className="metrics-treemap-title">

View File

@ -87,6 +87,7 @@ function Summary(): JSX.Element {
data: metricsData,
isLoading: isMetricsLoading,
isFetching: isMetricsFetching,
isError: isMetricsError,
} = useGetMetricsList(metricsListQuery, {
enabled: !!metricsListQuery,
});
@ -95,6 +96,7 @@ function Summary(): JSX.Element {
data: treeMapData,
isLoading: isTreeMapLoading,
isFetching: isTreeMapFetching,
isError: isTreeMapError,
} = useGetMetricsTreeMap(metricsTreemapQuery, {
enabled: !!metricsTreemapQuery,
});
@ -160,11 +162,13 @@ function Summary(): JSX.Element {
<MetricsTreemap
data={treeMapData?.payload}
isLoading={isTreeMapLoading || isTreeMapFetching}
isError={isTreeMapError}
viewType={heatmapView}
openMetricDetails={openMetricDetails}
/>
<MetricsTable
isLoading={isMetricsLoading || isMetricsFetching}
isError={isMetricsError}
data={formattedMetricsData}
pageSize={pageSize}
currentPage={currentPage}

View File

@ -7,6 +7,7 @@ import {
export interface MetricsTableProps {
isLoading: boolean;
isError: boolean;
data: MetricsListItemRowData[];
pageSize: number;
currentPage: number;
@ -26,6 +27,7 @@ export interface MetricsSearchProps {
export interface MetricsTreemapProps {
data: MetricsTreeMapResponse | null | undefined;
isLoading: boolean;
isError: boolean;
viewType: TreemapViewType;
openMetricDetails: (metricName: string) => void;
}

View File

@ -8,6 +8,7 @@ import {
Cloudy,
DraftingCompass,
FileKey2,
HardDrive,
Home,
Layers2,
LayoutGrid,
@ -88,7 +89,7 @@ const menuItems: SidebarItem[] = [
{
key: ROUTES.APPLICATION,
label: 'Services',
icon: <BarChart2 size={16} />,
icon: <HardDrive size={16} />,
},
{
key: ROUTES.TRACES_EXPLORER,

View File

@ -6,6 +6,7 @@ import { QueryParams } from 'constants/query';
import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch';
import LogsError from 'container/LogsError/LogsError';
import { LogsLoading } from 'container/LogsLoading/LogsLoading';
import EmptyMetricsSearch from 'container/MetricsExplorer/Explorer/EmptyMetricsSearch';
import { MetricsLoading } from 'container/MetricsExplorer/MetricsLoading/MetricsLoading';
import NoLogs from 'container/NoLogs/NoLogs';
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
@ -187,7 +188,15 @@ function TimeSeriesView({
chartData[0]?.length === 0 &&
!isLoading &&
!isError &&
!isFilterApplied && <NoLogs dataSource={dataSource} />}
!isFilterApplied &&
dataSource !== DataSource.METRICS && <NoLogs dataSource={dataSource} />}
{chartData &&
chartData[0] &&
chartData[0]?.length === 0 &&
!isLoading &&
!isError &&
dataSource === DataSource.METRICS && <EmptyMetricsSearch />}
{!isLoading &&
!isError &&

View File

@ -1,5 +1,6 @@
import {
getMetricsListFilterKeys,
GetMetricsListFilterKeysParams,
MetricsListFilterKeysResponse,
} from 'api/metricsExplorer/getMetricsListFilterKeys';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
@ -8,6 +9,7 @@ import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query';
import { ErrorResponse, SuccessResponse } from 'types/api';
type UseGetMetricsListFilterKeys = (
params: GetMetricsListFilterKeysParams,
options?: UseQueryOptions<
SuccessResponse<MetricsListFilterKeysResponse> | ErrorResponse,
Error
@ -19,6 +21,7 @@ type UseGetMetricsListFilterKeys = (
>;
export const useGetMetricsListFilterKeys: UseGetMetricsListFilterKeys = (
params,
options,
headers,
) => {
@ -38,7 +41,7 @@ export const useGetMetricsListFilterKeys: UseGetMetricsListFilterKeys = (
SuccessResponse<MetricsListFilterKeysResponse> | ErrorResponse,
Error
>({
queryFn: ({ signal }) => getMetricsListFilterKeys(signal, headers),
queryFn: ({ signal }) => getMetricsListFilterKeys(params, signal, headers),
...options,
queryKey,
});

View File

@ -129,7 +129,7 @@ export const useFetchKeysAndValues = (
},
{
queryKey: [searchParams],
enabled: isQueryEnabled && !shouldUseSuggestions,
enabled: isMetricsExplorer ? false : isQueryEnabled && !shouldUseSuggestions,
},
isInfraMonitoring, // isInfraMonitoring
entity, // infraMonitoringEntity
@ -155,9 +155,15 @@ export const useFetchKeysAndValues = (
data: metricsListFilterKeysData,
isFetching: isFetchingMetricsListFilterKeys,
status: fetchingMetricsListFilterKeysStatus,
} = useGetMetricsListFilterKeys({
enabled: isMetricsExplorer && isQueryEnabled && !shouldUseSuggestions,
});
} = useGetMetricsListFilterKeys(
{
searchText: searchKey,
},
{
enabled: isMetricsExplorer && isQueryEnabled && !shouldUseSuggestions,
queryKey: [searchKey],
},
);
function isAttributeValuesResponse(
payload: any,