diff --git a/frontend/src/container/AllError/constant.ts b/frontend/src/container/AllError/constant.ts new file mode 100644 index 0000000000..268f3ff89c --- /dev/null +++ b/frontend/src/container/AllError/constant.ts @@ -0,0 +1,9 @@ +const DEFAULT_FILTER_VALUE = ''; +const EXCEPTION_TYPE_FILTER_NAME = 'exceptionType'; +const SERVICE_NAME_FILTER_NAME = 'serviceName'; + +export { + DEFAULT_FILTER_VALUE, + EXCEPTION_TYPE_FILTER_NAME, + SERVICE_NAME_FILTER_NAME, +}; diff --git a/frontend/src/container/AllError/index.tsx b/frontend/src/container/AllError/index.tsx index 6db945b9bc..3b3939a4a7 100644 --- a/frontend/src/container/AllError/index.tsx +++ b/frontend/src/container/AllError/index.tsx @@ -17,6 +17,7 @@ import getAll from 'api/errors/getAll'; import getErrorCounts from 'api/errors/getErrorCounts'; import ROUTES from 'constants/routes'; import dayjs from 'dayjs'; +import useUrlQuery from 'hooks/useUrlQuery'; import createQueryParams from 'lib/createQueryParams'; import history from 'lib/history'; import React, { useCallback, useEffect, useMemo } from 'react'; @@ -30,7 +31,11 @@ import { Exception, PayloadProps } from 'types/api/errors/getAll'; import { GlobalReducer } from 'types/reducer/globalTime'; import { + extractFilterValues, + getDefaultFilterValue, getDefaultOrder, + getFilterString, + getFilterValues, getNanoSeconds, getOffSet, getOrder, @@ -43,15 +48,27 @@ function AllErrors(): JSX.Element { const { maxTime, minTime, loading } = useSelector( (state) => state.globalTime, ); - const { search, pathname } = useLocation(); - const params = useMemo(() => new URLSearchParams(search), [search]); - + const { pathname } = useLocation(); + const params = useUrlQuery(); const { t } = useTranslation(['common']); - - const updatedOrder = getOrder(params.get(urlKey.order)); - const getUpdatedOffset = getOffSet(params.get(urlKey.offset)); - const getUpdatedParams = getOrderParams(params.get(urlKey.orderParam)); - const getUpdatedPageSize = getUpdatePageSize(params.get(urlKey.pageSize)); + const { + updatedOrder, + getUpdatedOffset, + getUpdatedParams, + getUpdatedPageSize, + getUpdatedExceptionType, + getUpdatedServiceName, + } = useMemo( + () => ({ + updatedOrder: getOrder(params.get(urlKey.order)), + getUpdatedOffset: getOffSet(params.get(urlKey.offset)), + getUpdatedParams: getOrderParams(params.get(urlKey.orderParam)), + getUpdatedPageSize: getUpdatePageSize(params.get(urlKey.pageSize)), + getUpdatedExceptionType: getFilterString(params.get(urlKey.exceptionType)), + getUpdatedServiceName: getFilterString(params.get(urlKey.serviceName)), + }), + [params], + ); const updatedPath = useMemo( () => @@ -60,6 +77,8 @@ function AllErrors(): JSX.Element { offset: getUpdatedOffset, orderParam: getUpdatedParams, pageSize: getUpdatedPageSize, + exceptionType: getUpdatedExceptionType, + serviceName: getUpdatedServiceName, })}`, [ pathname, @@ -67,6 +86,8 @@ function AllErrors(): JSX.Element { getUpdatedOffset, getUpdatedParams, getUpdatedPageSize, + getUpdatedExceptionType, + getUpdatedServiceName, ], ); @@ -81,6 +102,8 @@ function AllErrors(): JSX.Element { limit: getUpdatedPageSize, offset: getUpdatedOffset, orderParam: getUpdatedParams, + exceptionType: getUpdatedExceptionType, + serviceName: getUpdatedServiceName, }), enabled: !loading, }, @@ -108,14 +131,43 @@ function AllErrors(): JSX.Element { const filterIcon = useCallback(() => , []); - const handleSearch = ( - confirm: (param?: FilterConfirmProps) => void, - ): VoidFunction => (): void => { - confirm(); - }; + const handleSearch = useCallback( + ( + confirm: (param?: FilterConfirmProps) => void, + filterValue: string, + filterKey: string, + ): VoidFunction => (): void => { + const { exceptionFilterValue, serviceFilterValue } = getFilterValues( + getUpdatedServiceName, + getUpdatedExceptionType, + filterKey, + filterValue, + ); + history.replace( + `${pathname}?${createQueryParams({ + order: updatedOrder, + offset: getUpdatedOffset, + orderParam: getUpdatedParams, + pageSize: getUpdatedPageSize, + exceptionType: exceptionFilterValue, + serviceName: serviceFilterValue, + })}`, + ); + confirm(); + }, + [ + getUpdatedExceptionType, + getUpdatedOffset, + getUpdatedPageSize, + getUpdatedParams, + getUpdatedServiceName, + pathname, + updatedOrder, + ], + ); const filterDropdownWrapper = useCallback( - ({ setSelectedKeys, selectedKeys, confirm, placeholder }) => { + ({ setSelectedKeys, selectedKeys, confirm, placeholder, filterKey }) => { return ( @@ -126,11 +178,16 @@ function AllErrors(): JSX.Element { setSelectedKeys(e.target.value ? [e.target.value] : []) } allowClear - onPressEnter={handleSearch(confirm)} + defaultValue={getDefaultFilterValue( + filterKey, + getUpdatedServiceName, + getUpdatedExceptionType, + )} + onPressEnter={handleSearch(confirm, selectedKeys[0], filterKey)} /> + + + ); +} +export default SearchFieldsActionBar; diff --git a/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx b/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx index bca82bdaaf..a7228c6c45 100644 --- a/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx +++ b/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx @@ -1,10 +1,11 @@ -import { Button, notification, Row } from 'antd'; +import { notification } from 'antd'; import { flatten } from 'lodash-es'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { ILogsReducer } from 'types/reducer/logs'; +import { SearchFieldsActionBar } from './ActionBar'; import QueryBuilder from './QueryBuilder/QueryBuilder'; import Suggestions from './Suggestions'; import { @@ -68,24 +69,26 @@ function SearchFields({ [fieldsQuery, setFieldsQuery], ); - const applyUpdate = useCallback( - (e): void => { - e.preventDefault(); - const flatParsedQuery = flatten(fieldsQuery); + const applyUpdate = useCallback((): void => { + const flatParsedQuery = flatten(fieldsQuery); - if (!fieldsQueryIsvalid(flatParsedQuery)) { - notification.error({ - message: 'Please enter a valid criteria for each of the selected fields', - }); - return; - } + if (!fieldsQueryIsvalid(flatParsedQuery)) { + notification.error({ + message: 'Please enter a valid criteria for each of the selected fields', + }); + return; + } - keyPrefixRef.current = hashCode(JSON.stringify(flatParsedQuery)); - updateParsedQuery(flatParsedQuery); - onDropDownToggleHandler(false)(); - }, - [onDropDownToggleHandler, fieldsQuery, updateParsedQuery], - ); + keyPrefixRef.current = hashCode(JSON.stringify(flatParsedQuery)); + updateParsedQuery(flatParsedQuery); + onDropDownToggleHandler(false)(); + }, [onDropDownToggleHandler, fieldsQuery, updateParsedQuery]); + + const clearFilters = useCallback((): void => { + keyPrefixRef.current = hashCode(JSON.stringify([])); + updateParsedQuery([]); + onDropDownToggleHandler(false)(); + }, [onDropDownToggleHandler, updateParsedQuery]); return ( <> @@ -96,11 +99,11 @@ function SearchFields({ fieldsQuery={fieldsQuery} setFieldsQuery={setFieldsQuery} /> - - - + ); diff --git a/frontend/src/types/api/errors/getAll.ts b/frontend/src/types/api/errors/getAll.ts index d0bbd7995a..38444cac62 100644 --- a/frontend/src/types/api/errors/getAll.ts +++ b/frontend/src/types/api/errors/getAll.ts @@ -15,6 +15,8 @@ export interface Props { orderParam?: OrderBy; limit?: number; offset?: number; + exceptionType?: string; + serviceName?: string; } export interface Exception { diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index a9626d887b..1d3116e24c 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -2528,8 +2528,35 @@ func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.Li var getErrorResponses []model.Error - query := fmt.Sprintf("SELECT any(exceptionType) as exceptionType, any(exceptionMessage) as exceptionMessage, count() AS exceptionCount, min(timestamp) as firstSeen, max(timestamp) as lastSeen, any(serviceName) as serviceName, groupID FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU GROUP BY groupID", r.TraceDB, r.errorTable) + query := "SELECT any(exceptionMessage) as exceptionMessage, count() AS exceptionCount, min(timestamp) as firstSeen, max(timestamp) as lastSeen, groupID" + if len(queryParams.ServiceName) != 0 { + query = query + ", serviceName" + } else { + query = query + ", any(serviceName) as serviceName" + } + if len(queryParams.ExceptionType) != 0 { + query = query + ", exceptionType" + } else { + query = query + ", any(exceptionType) as exceptionType" + } + query += fmt.Sprintf(" FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", r.TraceDB, r.errorTable) args := []interface{}{clickhouse.Named("timestampL", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), clickhouse.Named("timestampU", strconv.FormatInt(queryParams.End.UnixNano(), 10))} + + if len(queryParams.ServiceName) != 0 { + query = query + " AND serviceName ilike @serviceName" + args = append(args, clickhouse.Named("serviceName", "%"+queryParams.ServiceName+"%")) + } + if len(queryParams.ExceptionType) != 0 { + query = query + " AND exceptionType ilike @exceptionType" + args = append(args, clickhouse.Named("exceptionType", "%"+queryParams.ExceptionType+"%")) + } + query = query + " GROUP BY groupID" + if len(queryParams.ServiceName) != 0 { + query = query + ", serviceName" + } + if len(queryParams.ExceptionType) != 0 { + query = query + ", exceptionType" + } if len(queryParams.OrderParam) != 0 { if queryParams.Order == constants.Descending { query = query + " ORDER BY " + queryParams.OrderParam + " DESC" @@ -2564,7 +2591,14 @@ func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.C query := fmt.Sprintf("SELECT count(distinct(groupID)) FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", r.TraceDB, r.errorTable) args := []interface{}{clickhouse.Named("timestampL", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), clickhouse.Named("timestampU", strconv.FormatInt(queryParams.End.UnixNano(), 10))} - + if len(queryParams.ServiceName) != 0 { + query = query + " AND serviceName = @serviceName" + args = append(args, clickhouse.Named("serviceName", queryParams.ServiceName)) + } + if len(queryParams.ExceptionType) != 0 { + query = query + " AND exceptionType = @exceptionType" + args = append(args, clickhouse.Named("exceptionType", queryParams.ExceptionType)) + } err := r.db.QueryRow(ctx, query, args...).Scan(&errorCount) zap.S().Info(query) @@ -3233,7 +3267,8 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda // remove index query := fmt.Sprintf("ALTER TABLE %s.%s ON CLUSTER %s DROP INDEX IF EXISTS %s_idx", r.logsDB, r.logsLocalTable, cluster, field.Name) err := r.db.Exec(ctx, query) - if err != nil { + // we are ignoring errors with code 341 as it is an error with updating old part https://github.com/SigNoz/engineering-pod/issues/919#issuecomment-1366344346 + if err != nil && !strings.HasPrefix(err.Error(), "code: 341") { return &model.ApiError{Err: err, Typ: model.ErrorInternal} } } diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index fc3154c9fc..d14c0afc82 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -480,14 +480,18 @@ func parseListErrorsRequest(r *http.Request) (*model.ListErrorsParams, error) { if err != nil { return nil, errors.New("offset param is not in correct format") } + serviceName := r.URL.Query().Get("serviceName") + exceptionType := r.URL.Query().Get("exceptionType") params := &model.ListErrorsParams{ - Start: startTime, - End: endTime, - OrderParam: orderParam, - Order: order, - Limit: int64(limitInt), - Offset: int64(offsetInt), + Start: startTime, + End: endTime, + OrderParam: orderParam, + Order: order, + Limit: int64(limitInt), + Offset: int64(offsetInt), + ServiceName: serviceName, + ExceptionType: exceptionType, } return params, nil diff --git a/pkg/query-service/model/queryParams.go b/pkg/query-service/model/queryParams.go index 6d78e7438f..83d0e87935 100644 --- a/pkg/query-service/model/queryParams.go +++ b/pkg/query-service/model/queryParams.go @@ -296,17 +296,21 @@ type GetTTLParams struct { } type ListErrorsParams struct { - Start *time.Time - End *time.Time - Limit int64 - OrderParam string - Order string - Offset int64 + Start *time.Time + End *time.Time + Limit int64 + OrderParam string + Order string + Offset int64 + ServiceName string + ExceptionType string } type CountErrorsParams struct { - Start *time.Time - End *time.Time + Start *time.Time + End *time.Time + ServiceName string + ExceptionType string } type GetErrorParams struct {