mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 04:29:04 +08:00
feat: api monitoring feedback till 28th april
This commit is contained in:
parent
fd21a4955e
commit
6de0908a62
@ -1,6 +1,11 @@
|
||||
import { Select } from 'antd';
|
||||
import { getAllEndpointsWidgetData } from 'container/ApiMonitoring/utils';
|
||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import {
|
||||
getAllEndpointsWidgetData,
|
||||
getGroupByFiltersFromGroupByValues,
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import GridCard from 'container/GridCardLayout/GridCard';
|
||||
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
|
||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
@ -15,6 +20,8 @@ function AllEndPoints({
|
||||
groupBy,
|
||||
setGroupBy,
|
||||
timeRange,
|
||||
initialFilters,
|
||||
setInitialFiltersEndPointStats,
|
||||
}: {
|
||||
domainName: string;
|
||||
setSelectedEndPointName: (name: string) => void;
|
||||
@ -25,6 +32,8 @@ function AllEndPoints({
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
};
|
||||
initialFilters: IBuilderQuery['filters'];
|
||||
setInitialFiltersEndPointStats: (filters: IBuilderQuery['filters']) => void;
|
||||
}): JSX.Element {
|
||||
const {
|
||||
data: groupByFiltersData,
|
||||
@ -72,13 +81,81 @@ function AllEndPoints({
|
||||
}
|
||||
}, [groupByFiltersData]);
|
||||
|
||||
const currentQuery = initialQueriesMap[DataSource.TRACES];
|
||||
|
||||
// Local state for filters, combining endpoint filter and search filters
|
||||
const [filters, setFilters] = useState<IBuilderQuery['filters']>(() => {
|
||||
// Initialize filters based on the initial endPointName prop
|
||||
const initialItems = [...initialFilters.items];
|
||||
return { op: 'AND', items: initialItems };
|
||||
});
|
||||
|
||||
// Handler for changes from the QueryBuilderSearchV2 component
|
||||
const handleFilterChange = useCallback(
|
||||
(newFilters: IBuilderQuery['filters']): void => {
|
||||
// 1. Update local filters state immediately
|
||||
setFilters(newFilters);
|
||||
},
|
||||
[], // Dependencies for the callback
|
||||
);
|
||||
|
||||
const updatedCurrentQuery = useMemo(
|
||||
() => ({
|
||||
...currentQuery,
|
||||
builder: {
|
||||
...currentQuery.builder,
|
||||
queryData: [
|
||||
{
|
||||
...currentQuery.builder.queryData[0],
|
||||
dataSource: DataSource.TRACES,
|
||||
filters, // Use the local filters state
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
[filters, currentQuery],
|
||||
);
|
||||
|
||||
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
|
||||
|
||||
const allEndpointsWidgetData = useMemo(
|
||||
() => getAllEndpointsWidgetData(groupBy, domainName),
|
||||
[groupBy, domainName],
|
||||
() => getAllEndpointsWidgetData(groupBy, domainName, filters),
|
||||
[groupBy, domainName, filters],
|
||||
);
|
||||
|
||||
const onRowClick = useCallback(
|
||||
(props: any): void => {
|
||||
setSelectedEndPointName(props[SPAN_ATTRIBUTES.URL_PATH] as string);
|
||||
setSelectedView(VIEWS.ENDPOINT_STATS);
|
||||
const initialItems = [
|
||||
...filters.items,
|
||||
...getGroupByFiltersFromGroupByValues(props, groupBy).items,
|
||||
];
|
||||
setInitialFiltersEndPointStats({
|
||||
items: initialItems,
|
||||
op: 'AND',
|
||||
});
|
||||
},
|
||||
[
|
||||
filters,
|
||||
setInitialFiltersEndPointStats,
|
||||
setSelectedEndPointName,
|
||||
setSelectedView,
|
||||
groupBy,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="all-endpoints-container">
|
||||
<div className="all-endpoints-header">
|
||||
<div className="filter-container">
|
||||
<QueryBuilderSearchV2
|
||||
query={query}
|
||||
onChange={handleFilterChange}
|
||||
placeholder="Search for filters..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="group-by-container">
|
||||
<div className="group-by-label"> Group by </div>
|
||||
<Select
|
||||
@ -100,10 +177,7 @@ function AllEndPoints({
|
||||
onDragSelect={(): void => {}}
|
||||
customOnDragSelect={(): void => {}}
|
||||
customTimeRange={timeRange}
|
||||
customOnRowClick={(props): void => {
|
||||
setSelectedEndPointName(props[SPAN_ATTRIBUTES.URL_PATH] as string);
|
||||
setSelectedView(VIEWS.ENDPOINT_STATS);
|
||||
}}
|
||||
customOnRowClick={onRowClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,6 +45,9 @@ function DomainDetails({
|
||||
const [endPointsGroupBy, setEndPointsGroupBy] = useState<
|
||||
IBuilderQuery['groupBy']
|
||||
>([]);
|
||||
const [initialFiltersEndPointStats, setInitialFiltersEndPointStats] = useState<
|
||||
IBuilderQuery['filters']
|
||||
>(domainListFilters);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const handleTabChange = (e: RadioChangeEvent): void => {
|
||||
@ -160,6 +163,7 @@ function DomainDetails({
|
||||
<>
|
||||
<DomainMetrics
|
||||
domainName={domainData.domainName}
|
||||
domainListFilters={domainListFilters}
|
||||
timeRange={modalTimeRange}
|
||||
/>
|
||||
<div className="views-tabs-container">
|
||||
@ -205,6 +209,8 @@ function DomainDetails({
|
||||
groupBy={endPointsGroupBy}
|
||||
setGroupBy={setEndPointsGroupBy}
|
||||
timeRange={modalTimeRange}
|
||||
initialFilters={domainListFilters}
|
||||
setInitialFiltersEndPointStats={setInitialFiltersEndPointStats}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -213,7 +219,7 @@ function DomainDetails({
|
||||
domainName={domainData.domainName}
|
||||
endPointName={selectedEndPointName}
|
||||
setSelectedEndPointName={setSelectedEndPointName}
|
||||
domainListFilters={domainListFilters}
|
||||
initialFilters={initialFiltersEndPointStats}
|
||||
timeRange={modalTimeRange}
|
||||
handleTimeChange={handleTimeChange}
|
||||
/>
|
||||
@ -222,7 +228,9 @@ function DomainDetails({
|
||||
{selectedView === VIEW_TYPES.TOP_ERRORS && (
|
||||
<TopErrors
|
||||
domainName={domainData.domainName}
|
||||
initialFilters={domainListFilters}
|
||||
timeRange={modalTimeRange}
|
||||
handleTimeChange={handleTimeChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -41,14 +41,14 @@ function EndPointDetails({
|
||||
domainName,
|
||||
endPointName,
|
||||
setSelectedEndPointName,
|
||||
domainListFilters,
|
||||
initialFilters,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
}: {
|
||||
domainName: string;
|
||||
endPointName: string;
|
||||
setSelectedEndPointName: (value: string) => void;
|
||||
domainListFilters: IBuilderQuery['filters'];
|
||||
initialFilters: IBuilderQuery['filters'];
|
||||
timeRange: {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
@ -65,7 +65,7 @@ function EndPointDetails({
|
||||
// Local state for filters, combining endpoint filter and search filters
|
||||
const [filters, setFilters] = useState<IBuilderQuery['filters']>(() => {
|
||||
// Initialize filters based on the initial endPointName prop
|
||||
const initialItems = [];
|
||||
const initialItems = [...initialFilters.items];
|
||||
if (endPointName) {
|
||||
initialItems.push({
|
||||
id: '92b8a1c1',
|
||||
@ -196,21 +196,12 @@ function EndPointDetails({
|
||||
[endPointName],
|
||||
);
|
||||
|
||||
// Combine domainListFilters (from parent) and local filters for graph/chart queries
|
||||
const combinedFilters = useMemo(
|
||||
() => ({
|
||||
op: 'AND', // Assuming AND logic for combining filter sets
|
||||
items: [...domainListFilters.items, ...filters.items],
|
||||
}),
|
||||
[domainListFilters, filters],
|
||||
);
|
||||
|
||||
const [rateOverTimeWidget, latencyOverTimeWidget] = useMemo(
|
||||
() => [
|
||||
getRateOverTimeWidgetData(domainName, endPointName, combinedFilters),
|
||||
getLatencyOverTimeWidgetData(domainName, endPointName, combinedFilters),
|
||||
getRateOverTimeWidgetData(domainName, endPointName, filters),
|
||||
getLatencyOverTimeWidgetData(domainName, endPointName, filters),
|
||||
],
|
||||
[domainName, endPointName, combinedFilters], // Use combinedFilters
|
||||
[domainName, endPointName, filters], // Use combinedFilters
|
||||
);
|
||||
|
||||
// // [TODO] Fix this later
|
||||
@ -273,7 +264,6 @@ function EndPointDetails({
|
||||
}
|
||||
domainName={domainName}
|
||||
endPointName={endPointName}
|
||||
domainListFilters={domainListFilters}
|
||||
filters={filters}
|
||||
timeRange={timeRange}
|
||||
onDragSelect={onDragSelect}
|
||||
|
@ -10,13 +10,18 @@ import {
|
||||
getTopErrorsQueryPayload,
|
||||
TopErrorsResponseRow,
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import {
|
||||
CustomTimeType,
|
||||
Time,
|
||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
|
||||
import { Info } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import EndPointsDropDown from './components/EndPointsDropDown';
|
||||
import ErrorState from './components/ErrorState';
|
||||
@ -25,12 +30,19 @@ import { SPAN_ATTRIBUTES } from './constants';
|
||||
function TopErrors({
|
||||
domainName,
|
||||
timeRange,
|
||||
initialFilters,
|
||||
handleTimeChange,
|
||||
}: {
|
||||
domainName: string;
|
||||
timeRange: {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
};
|
||||
initialFilters: IBuilderQuery['filters'];
|
||||
handleTimeChange: (
|
||||
interval: Time | CustomTimeType,
|
||||
dateTimeRange?: [number, number],
|
||||
) => void;
|
||||
}): JSX.Element {
|
||||
const { startTime: minTime, endTime: maxTime } = timeRange;
|
||||
|
||||
@ -53,11 +65,12 @@ function TopErrors({
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
...initialFilters.items,
|
||||
]
|
||||
: [],
|
||||
op: 'AND',
|
||||
}),
|
||||
[domainName, endPointName, minTime, maxTime],
|
||||
[domainName, endPointName, minTime, maxTime, initialFilters],
|
||||
);
|
||||
|
||||
// Since only one query here
|
||||
@ -123,6 +136,10 @@ function TopErrors({
|
||||
[endPointDropDownDataQueries],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
handleTimeChange('6h');
|
||||
}, [handleTimeChange]);
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="all-endpoints-error-state-wrapper">
|
||||
|
@ -12,21 +12,30 @@ import { useMemo } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import ErrorState from './ErrorState';
|
||||
|
||||
function DomainMetrics({
|
||||
domainName,
|
||||
timeRange,
|
||||
domainListFilters,
|
||||
}: {
|
||||
domainName: string;
|
||||
timeRange: { startTime: number; endTime: number };
|
||||
domainListFilters: IBuilderQuery['filters'];
|
||||
}): JSX.Element {
|
||||
const { startTime: minTime, endTime: maxTime } = timeRange;
|
||||
|
||||
const queryPayloads = useMemo(
|
||||
() => getDomainMetricsQueryPayload(domainName, minTime, maxTime),
|
||||
[domainName, minTime, maxTime],
|
||||
() =>
|
||||
getDomainMetricsQueryPayload(
|
||||
domainName,
|
||||
minTime,
|
||||
maxTime,
|
||||
domainListFilters,
|
||||
),
|
||||
[domainName, minTime, maxTime, domainListFilters],
|
||||
);
|
||||
|
||||
// Since only one query here
|
||||
|
@ -33,7 +33,6 @@ function StatusCodeBarCharts({
|
||||
endPointStatusCodeLatencyBarChartsDataQuery,
|
||||
domainName,
|
||||
endPointName,
|
||||
domainListFilters,
|
||||
filters,
|
||||
timeRange,
|
||||
onDragSelect,
|
||||
@ -48,7 +47,6 @@ function StatusCodeBarCharts({
|
||||
>;
|
||||
domainName: string;
|
||||
endPointName: string;
|
||||
domainListFilters: IBuilderQuery['filters'];
|
||||
filters: IBuilderQuery['filters'];
|
||||
timeRange: {
|
||||
startTime: number;
|
||||
@ -132,10 +130,10 @@ function StatusCodeBarCharts({
|
||||
const widget = useMemo<Widgets>(
|
||||
() =>
|
||||
getStatusCodeBarChartWidgetData(domainName, endPointName, {
|
||||
items: [...domainListFilters.items, ...filters.items],
|
||||
items: [...filters.items],
|
||||
op: filters.op,
|
||||
}),
|
||||
[domainName, endPointName, domainListFilters, filters],
|
||||
[domainName, endPointName, filters],
|
||||
);
|
||||
|
||||
const graphClickHandler = useCallback(
|
||||
|
@ -39,7 +39,9 @@ function DomainList({ showIP }: { showIP: boolean }): JSX.Element {
|
||||
);
|
||||
|
||||
const { currentQuery, handleRunQuery } = useQueryBuilder();
|
||||
const query = currentQuery?.builder?.queryData[0] || null;
|
||||
const query = useMemo(() => currentQuery?.builder?.queryData[0] || null, [
|
||||
currentQuery,
|
||||
]);
|
||||
|
||||
const { handleChangeQueryData } = useQueryOperations({
|
||||
index: 0,
|
||||
@ -96,7 +98,7 @@ function DomainList({ showIP }: { showIP: boolean }): JSX.Element {
|
||||
op: '=',
|
||||
value: 'Client',
|
||||
},
|
||||
...(query?.filters.items || []),
|
||||
...(compositeData?.builder?.queryData[0]?.filters.items || []),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -339,6 +339,7 @@ export const getDomainMetricsQueryPayload = (
|
||||
domainName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
filters: IBuilderQuery['filters'],
|
||||
): GetQueryResultsProps[] => [
|
||||
{
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
@ -374,6 +375,7 @@ export const getDomainMetricsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -415,6 +417,7 @@ export const getDomainMetricsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -468,6 +471,7 @@ export const getDomainMetricsQueryPayload = (
|
||||
op: '=',
|
||||
value: 'true',
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -509,6 +513,7 @@ export const getDomainMetricsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -1379,9 +1384,10 @@ export const getTopErrorsColumnsConfig = (): ColumnType<TopErrorsTableRowData>[]
|
||||
ellipsis: true,
|
||||
sorter: false,
|
||||
className: 'column',
|
||||
render: (text: string, record: TopErrorsTableRowData): React.ReactNode => (
|
||||
<div className="endpoint-name-value">{record.endpointName}</div>
|
||||
),
|
||||
render: (text: string, record: TopErrorsTableRowData): React.ReactNode => {
|
||||
const { endpoint } = extractPortAndEndpoint(record.endpointName);
|
||||
return <div className="endpoint-name-value">{endpoint}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: <div className="column-header">Status code</div>,
|
||||
@ -2730,7 +2736,7 @@ export const endPointStatusCodeColumns: ColumnType<EndPointStatusCodeData>[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'P99',
|
||||
title: 'P99 Latency',
|
||||
dataIndex: 'p99Latency',
|
||||
key: 'p99Latency',
|
||||
align: 'right',
|
||||
@ -2741,6 +2747,7 @@ export const endPointStatusCodeColumns: ColumnType<EndPointStatusCodeData>[] = [
|
||||
b.p99Latency === '-' || b.p99Latency === 'n/a' ? 0 : Number(b.p99Latency);
|
||||
return p99LatencyA - p99LatencyB;
|
||||
},
|
||||
render: (latency: number): ReactNode => <span>{latency || '-'}ms</span>,
|
||||
},
|
||||
];
|
||||
|
||||
@ -3222,6 +3229,7 @@ export const END_POINT_DETAILS_QUERY_KEYS_ARRAY = [
|
||||
export const getAllEndpointsWidgetData = (
|
||||
groupBy: BaseAutocompleteData[],
|
||||
domainName: string,
|
||||
filters: IBuilderQuery['filters'],
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
): Widgets => {
|
||||
const isGroupedByAttribute = groupBy.length > 0;
|
||||
@ -3270,6 +3278,7 @@ export const getAllEndpointsWidgetData = (
|
||||
op: '=',
|
||||
value: 'Client',
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -3325,6 +3334,7 @@ export const getAllEndpointsWidgetData = (
|
||||
op: '=',
|
||||
value: 'Client',
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -3380,6 +3390,7 @@ export const getAllEndpointsWidgetData = (
|
||||
op: '=',
|
||||
value: 'Client',
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -3447,6 +3458,7 @@ export const getAllEndpointsWidgetData = (
|
||||
op: '=',
|
||||
value: 'Client',
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
@ -3479,9 +3491,12 @@ export const getAllEndpointsWidgetData = (
|
||||
|
||||
widget.renderColumnCell = {
|
||||
[SPAN_ATTRIBUTES.URL_PATH]: (url: any): ReactNode => {
|
||||
const { endpoint } = extractPortAndEndpoint(url);
|
||||
const { endpoint, port } = extractPortAndEndpoint(url);
|
||||
return (
|
||||
<span>{endpoint === 'n/a' || url === undefined ? '-' : endpoint}</span>
|
||||
<span>
|
||||
{port !== '-' && port !== 'n/a' ? `:${port}` : ''}
|
||||
{endpoint === 'n/a' || url === undefined ? '-' : endpoint}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
A: (numOfCalls: any): ReactNode => (
|
||||
@ -3563,6 +3578,37 @@ export const getAllEndpointsWidgetData = (
|
||||
return widget;
|
||||
};
|
||||
|
||||
const keysToRemove = ['http.url', 'A', 'B', 'C', 'F1'];
|
||||
|
||||
export const getGroupByFiltersFromGroupByValues = (
|
||||
rowData: any,
|
||||
groupBy: BaseAutocompleteData[],
|
||||
): IBuilderQuery['filters'] => {
|
||||
const items = Object.keys(rowData)
|
||||
.filter((key) => !keysToRemove.includes(key))
|
||||
.map((key) => {
|
||||
const groupByAttribute = groupBy.find((gb) => gb.key === key);
|
||||
return {
|
||||
id: groupByAttribute?.id || v4(),
|
||||
key: {
|
||||
dataType: groupByAttribute?.dataType || DataTypes.String,
|
||||
isColumn: groupByAttribute?.isColumn || true,
|
||||
isJSON: groupByAttribute?.isJSON || false,
|
||||
key: groupByAttribute?.key || key,
|
||||
type: groupByAttribute?.type || '',
|
||||
},
|
||||
op: '=', // operator for every attribute -> discuss
|
||||
value: rowData[key],
|
||||
};
|
||||
});
|
||||
|
||||
console.log('uncaught items', items);
|
||||
return {
|
||||
items,
|
||||
op: 'AND',
|
||||
};
|
||||
};
|
||||
|
||||
export const getRateOverTimeWidgetData = (
|
||||
domainName: string,
|
||||
endPointName: string,
|
||||
|
Loading…
x
Reference in New Issue
Block a user