diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index d3ebee0492..e3e5fc47d7 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1464,13 +1464,13 @@ func createTagQueryFromTagQueryParams(queryParams []model.TagQueryParam) []model tags := []model.TagQuery{} for _, tag := range queryParams { if len(tag.StringValues) > 0 { - tags = append(tags, model.NewTagQueryString(tag.Key, tag.StringValues, tag.Operator)) + tags = append(tags, model.NewTagQueryString(tag)) } if len(tag.NumberValues) > 0 { - tags = append(tags, model.NewTagQueryNumber(tag.Key, tag.NumberValues, tag.Operator)) + tags = append(tags, model.NewTagQueryNumber(tag)) } if len(tag.BoolValues) > 0 { - tags = append(tags, model.NewTagQueryBool(tag.Key, tag.BoolValues, tag.Operator)) + tags = append(tags, model.NewTagQueryBool(tag)) } } return tags @@ -1494,18 +1494,7 @@ func buildQueryWithTagParams(ctx context.Context, tags []model.TagQuery) (string for _, item := range tags { var subQuery string var argsSubQuery []interface{} - tagMapType := "" - switch item.(type) { - case model.TagQueryString: - tagMapType = constants.StringTagMapCol - case model.TagQueryNumber: - tagMapType = constants.NumberTagMapCol - case model.TagQueryBool: - tagMapType = constants.BoolTagMapCol - default: - // type not supported error - return "", nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("type not supported")} - } + tagMapType := item.GetTagMapColumn() switch item.GetOperator() { case model.EqualOperator: subQuery, argsSubQuery = addArithmeticOperator(item, tagMapType, "=") @@ -2698,6 +2687,17 @@ func (r *ClickHouseReader) ListErrors(ctx context.Context, queryParams *model.Li query = query + " AND exceptionType ilike @exceptionType" args = append(args, clickhouse.Named("exceptionType", "%"+queryParams.ExceptionType+"%")) } + + // create TagQuery from TagQueryParams + tags := createTagQueryFromTagQueryParams(queryParams.Tags) + subQuery, argsSubQuery, errStatus := buildQueryWithTagParams(ctx, tags) + query += subQuery + args = append(args, argsSubQuery...) + + if errStatus != nil { + zap.S().Error("Error in processing tags: ", errStatus) + return nil, errStatus + } query = query + " GROUP BY groupID" if len(queryParams.ServiceName) != 0 { query = query + ", serviceName" @@ -2747,6 +2747,18 @@ func (r *ClickHouseReader) CountErrors(ctx context.Context, queryParams *model.C query = query + " AND exceptionType ilike @exceptionType" args = append(args, clickhouse.Named("exceptionType", "%"+queryParams.ExceptionType+"%")) } + + // create TagQuery from TagQueryParams + tags := createTagQueryFromTagQueryParams(queryParams.Tags) + subQuery, argsSubQuery, errStatus := buildQueryWithTagParams(ctx, tags) + query += subQuery + args = append(args, argsSubQuery...) + + if errStatus != nil { + zap.S().Error("Error in processing tags: ", errStatus) + return 0, errStatus + } + err := r.db.QueryRow(ctx, query, args...).Scan(&errorCount) zap.S().Info(query) diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 7a6097638a..a617201233 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -328,8 +328,8 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) { router.HandleFunc("/api/v1/getFilteredSpans/aggregates", am.ViewAccess(aH.getFilteredSpanAggregates)).Methods(http.MethodPost) router.HandleFunc("/api/v1/getTagValues", am.ViewAccess(aH.getTagValues)).Methods(http.MethodPost) - router.HandleFunc("/api/v1/listErrors", am.ViewAccess(aH.listErrors)).Methods(http.MethodGet) - router.HandleFunc("/api/v1/countErrors", am.ViewAccess(aH.countErrors)).Methods(http.MethodGet) + router.HandleFunc("/api/v1/listErrors", am.ViewAccess(aH.listErrors)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/countErrors", am.ViewAccess(aH.countErrors)).Methods(http.MethodPost) router.HandleFunc("/api/v1/errorFromErrorID", am.ViewAccess(aH.getErrorFromErrorID)).Methods(http.MethodGet) router.HandleFunc("/api/v1/errorFromGroupID", am.ViewAccess(aH.getErrorFromGroupID)).Methods(http.MethodGet) router.HandleFunc("/api/v1/nextPrevErrorIDs", am.ViewAccess(aH.getNextPrevErrorIDs)).Methods(http.MethodGet) diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index a35f3138de..5c4393ae7a 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -494,76 +494,54 @@ func parseListErrorsRequest(r *http.Request) (*model.ListErrorsParams, error) { var allowedOrderParams = []string{"exceptionType", "exceptionCount", "firstSeen", "lastSeen", "serviceName"} var allowedOrderDirections = []string{"ascending", "descending"} - startTime, err := parseTime("start", r) - if err != nil { - return nil, err - } - endTime, err := parseTimeMinusBuffer("end", r) + var postData *model.ListErrorsParams + err := json.NewDecoder(r.Body).Decode(&postData) + if err != nil { return nil, err } - order := r.URL.Query().Get("order") - if len(order) > 0 && !DoesExistInSlice(order, allowedOrderDirections) { - return nil, errors.New(fmt.Sprintf("given order: %s is not allowed in query", order)) - } - orderParam := r.URL.Query().Get("orderParam") - if len(order) > 0 && !DoesExistInSlice(orderParam, allowedOrderParams) { - return nil, errors.New(fmt.Sprintf("given orderParam: %s is not allowed in query", orderParam)) - } - limit := r.URL.Query().Get("limit") - offset := r.URL.Query().Get("offset") - - if len(offset) == 0 || len(limit) == 0 { - return nil, fmt.Errorf("offset or limit param cannot be empty from the query") - } - - limitInt, err := strconv.Atoi(limit) + postData.Start, err = parseTimeStr(postData.StartStr, "start") if err != nil { - return nil, errors.New("limit param is not in correct format") + return nil, err } - offsetInt, err := strconv.Atoi(offset) + postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") if err != nil { - return nil, errors.New("offset param is not in correct format") + return nil, err } - 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), - ServiceName: serviceName, - ExceptionType: exceptionType, + if postData.Limit == 0 { + return nil, fmt.Errorf("limit param cannot be empty from the query") } - return params, nil + if len(postData.Order) > 0 && !DoesExistInSlice(postData.Order, allowedOrderDirections) { + return nil, errors.New(fmt.Sprintf("given order: %s is not allowed in query", postData.Order)) + } + + if len(postData.Order) > 0 && !DoesExistInSlice(postData.OrderParam, allowedOrderParams) { + return nil, errors.New(fmt.Sprintf("given orderParam: %s is not allowed in query", postData.OrderParam)) + } + + return postData, nil } func parseCountErrorsRequest(r *http.Request) (*model.CountErrorsParams, error) { - startTime, err := parseTime("start", r) + var postData *model.CountErrorsParams + err := json.NewDecoder(r.Body).Decode(&postData) + if err != nil { return nil, err } - endTime, err := parseTimeMinusBuffer("end", r) + + postData.Start, err = parseTimeStr(postData.StartStr, "start") if err != nil { return nil, err } - serviceName := r.URL.Query().Get("serviceName") - exceptionType := r.URL.Query().Get("exceptionType") - - params := &model.CountErrorsParams{ - Start: startTime, - End: endTime, - ServiceName: serviceName, - ExceptionType: exceptionType, + postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") + if err != nil { + return nil, err } - - return params, nil + return postData, nil } func parseGetErrorRequest(r *http.Request) (*model.GetErrorParams, error) { diff --git a/pkg/query-service/constants/constants.go b/pkg/query-service/constants/constants.go index d433163aa7..ce03e364e1 100644 --- a/pkg/query-service/constants/constants.go +++ b/pkg/query-service/constants/constants.go @@ -219,11 +219,5 @@ var ReservedColumnTargetAliases = map[string]struct{}{ "value": {}, } -const ( - StringTagMapCol = "stringTagMap" - NumberTagMapCol = "numberTagMap" - BoolTagMapCol = "boolTagMap" -) - // logsPPLPfx is a short constant for logsPipelinePrefix const LogsPPLPfx = "logstransform/pipeline_" diff --git a/pkg/query-service/model/queryParams.go b/pkg/query-service/model/queryParams.go index 4730b1fabf..a94f57ad02 100644 --- a/pkg/query-service/model/queryParams.go +++ b/pkg/query-service/model/queryParams.go @@ -122,6 +122,13 @@ const ( LOGS ) +const ( + StringTagMapCol = "stringTagMap" + NumberTagMapCol = "numberTagMap" + BoolTagMapCol = "boolTagMap" + ResourceTagMapCol = "resourceTagsMap" +) + type QueryRangeParamsV2 struct { DataSource DataSource `json:"dataSource"` Start int64 `json:"start"` @@ -187,6 +194,7 @@ type GetServiceOverviewParams struct { type TagQueryParam struct { Key string `json:"key"` + TagType TagType `json:"tagType"` StringValues []string `json:"stringValues"` BoolValues []bool `json:"boolValues"` NumberValues []float64 `json:"numberValues"` @@ -212,23 +220,34 @@ const ( NotStartsWithOperator Operator = "NotStartsWith" ) +type TagType string + +const ( + ResourceAttributeTagType TagType = "ResourceAttribute" + SpanAttributeTagType TagType = "SpanAttribute" +) + type TagQuery interface { GetKey() string GetValues() []interface{} GetOperator() Operator + GetTagType() TagType + GetTagMapColumn() string } type TagQueryString struct { key string values []string operator Operator + tagType TagType } -func NewTagQueryString(key string, values []string, operator Operator) TagQueryString { +func NewTagQueryString(tag TagQueryParam) TagQueryString { return TagQueryString{ - key: key, - values: values, - operator: operator, + key: tag.Key, + values: tag.StringValues, + operator: tag.Operator, + tagType: tag.TagType, } } @@ -248,17 +267,31 @@ func (tqs TagQueryString) GetOperator() Operator { return tqs.operator } +func (tqs TagQueryString) GetTagType() TagType { + return tqs.tagType +} + +func (tqs TagQueryString) GetTagMapColumn() string { + if tqs.GetTagType() == ResourceAttributeTagType { + return ResourceTagMapCol + } else { + return StringTagMapCol + } +} + type TagQueryBool struct { key string values []bool operator Operator + tagType TagType } -func NewTagQueryBool(key string, values []bool, operator Operator) TagQueryBool { +func NewTagQueryBool(tag TagQueryParam) TagQueryBool { return TagQueryBool{ - key: key, - values: values, - operator: operator, + key: tag.Key, + values: tag.BoolValues, + operator: tag.Operator, + tagType: tag.TagType, } } @@ -278,17 +311,27 @@ func (tqb TagQueryBool) GetOperator() Operator { return tqb.operator } +func (tqb TagQueryBool) GetTagType() TagType { + return tqb.tagType +} + +func (tqb TagQueryBool) GetTagMapColumn() string { + return BoolTagMapCol +} + type TagQueryNumber struct { key string values []float64 operator Operator + tagType TagType } -func NewTagQueryNumber(key string, values []float64, operator Operator) TagQueryNumber { +func NewTagQueryNumber(tag TagQueryParam) TagQueryNumber { return TagQueryNumber{ - key: key, - values: values, - operator: operator, + key: tag.Key, + values: tag.NumberValues, + operator: tag.Operator, + tagType: tag.TagType, } } @@ -308,6 +351,14 @@ func (tqn TagQueryNumber) GetOperator() Operator { return tqn.operator } +func (tqn TagQueryNumber) GetTagType() TagType { + return tqn.tagType +} + +func (tqn TagQueryNumber) GetTagMapColumn() string { + return NumberTagMapCol +} + type GetFilteredSpansParams struct { TraceID []string `json:"traceID"` ServiceName []string `json:"serviceName"` @@ -414,17 +465,17 @@ type TagFilterParams struct { End *time.Time } -type TagType string +type TagDataType string const ( - TagTypeString TagType = "string" - TagTypeNumber TagType = "number" - TagTypeBool TagType = "bool" + TagTypeString TagDataType = "string" + TagTypeNumber TagDataType = "number" + TagTypeBool TagDataType = "bool" ) type TagKey struct { - Key string `json:"key"` - Type TagType `json:"type"` + Key string `json:"key"` + Type TagDataType `json:"type"` } type TTLParams struct { @@ -439,21 +490,27 @@ type GetTTLParams struct { } type ListErrorsParams struct { + StartStr string `json:"start"` + EndStr string `json:"end"` Start *time.Time End *time.Time - Limit int64 - OrderParam string - Order string - Offset int64 - ServiceName string - ExceptionType string + Limit int64 `json:"limit"` + OrderParam string `json:"orderParam"` + Order string `json:"order"` + Offset int64 `json:"offset"` + ServiceName string `json:"serviceName"` + ExceptionType string `json:"exceptionType"` + Tags []TagQueryParam `json:"tags"` } type CountErrorsParams struct { + StartStr string `json:"start"` + EndStr string `json:"end"` Start *time.Time End *time.Time - ServiceName string - ExceptionType string + ServiceName string `json:"serviceName"` + ExceptionType string `json:"exceptionType"` + Tags []TagQueryParam `json:"tags"` } type GetErrorParams struct {