Fix 414 errors trace filter API (#660)

* fix: change trace filter APIs method from GET to POST

* fix: error filter param

* fix: json of aggregate params
This commit is contained in:
Vishal Sharma 2022-02-02 11:40:30 +05:30 committed by GitHub
parent 24f1404741
commit fdd9287847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 185 additions and 338 deletions

View File

@ -1622,13 +1622,12 @@ func (r *ClickHouseReader) GetFilteredSpans(ctx context.Context, queryParams *mo
query = query + " AND durationNano <= ?" query = query + " AND durationNano <= ?"
args = append(args, queryParams.MaxDuration) args = append(args, queryParams.MaxDuration)
} }
if len(queryParams.Status) != 0 { // status can only be two and if both are selected than they are equivalent to none selected
for _, e := range queryParams.Status { if len(queryParams.Status) == 1 {
if e == "error" { if queryParams.Status[0] == "error" {
query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))" query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))"
} else if e == "ok" { } else if queryParams.Status[0] == "ok" {
query += " AND (NOT ( has(tags, 'error:true') AND statusCode<500 AND statusCode!=2))" query += " AND ((NOT ( has(tags, 'error:true')) AND statusCode<500 AND statusCode!=2))"
}
} }
} }
if len(queryParams.Kind) != 0 { if len(queryParams.Kind) != 0 {
@ -1776,13 +1775,12 @@ func (r *ClickHouseReader) GetTagFilters(ctx context.Context, queryParams *model
query = query + " AND durationNano <= ?" query = query + " AND durationNano <= ?"
args = append(args, queryParams.MaxDuration) args = append(args, queryParams.MaxDuration)
} }
if len(queryParams.Status) != 0 { // status can only be two and if both are selected than they are equivalent to none selected
for _, e := range queryParams.Status { if len(queryParams.Status) == 1 {
if e == "error" { if queryParams.Status[0] == "error" {
query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))" query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))"
} else if e == "ok" { } else if queryParams.Status[0] == "ok" {
query += " AND (NOT ( has(tags, 'error:true') AND statusCode<500 AND statusCode!=2))" query += " AND ((NOT ( has(tags, 'error:true')) AND statusCode<500 AND statusCode!=2))"
}
} }
} }
tagFilters := []model.TagFilters{} tagFilters := []model.TagFilters{}
@ -2361,13 +2359,12 @@ func (r *ClickHouseReader) GetFilteredSpansAggregates(ctx context.Context, query
query = query + " AND durationNano <= ?" query = query + " AND durationNano <= ?"
args = append(args, queryParams.MaxDuration) args = append(args, queryParams.MaxDuration)
} }
if len(queryParams.Status) != 0 { // status can only be two and if both are selected than they are equivalent to none selected
for _, e := range queryParams.Status { if len(queryParams.Status) == 1 {
if e == "error" { if queryParams.Status[0] == "error" {
query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))" query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))"
} else if e == "ok" { } else if queryParams.Status[0] == "ok" {
query += " AND (NOT ( has(tags, 'error:true') AND statusCode<500 AND statusCode!=2))" query += " AND ((NOT ( has(tags, 'error:true')) AND statusCode<500 AND statusCode!=2))"
}
} }
} }
if len(queryParams.Kind) != 0 { if len(queryParams.Kind) != 0 {

View File

@ -209,10 +209,10 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) {
router.HandleFunc("/api/v1/userPreferences", aH.getUserPreferences).Methods(http.MethodGet) router.HandleFunc("/api/v1/userPreferences", aH.getUserPreferences).Methods(http.MethodGet)
router.HandleFunc("/api/v1/version", aH.getVersion).Methods(http.MethodGet) router.HandleFunc("/api/v1/version", aH.getVersion).Methods(http.MethodGet)
router.HandleFunc("/api/v1/getSpanFilters", aH.getSpanFilters).Methods(http.MethodGet) router.HandleFunc("/api/v1/getSpanFilters", aH.getSpanFilters).Methods(http.MethodPost)
router.HandleFunc("/api/v1/getTagFilters", aH.getTagFilters).Methods(http.MethodGet) router.HandleFunc("/api/v1/getTagFilters", aH.getTagFilters).Methods(http.MethodPost)
router.HandleFunc("/api/v1/getFilteredSpans", aH.getFilteredSpans).Methods(http.MethodGet) router.HandleFunc("/api/v1/getFilteredSpans", aH.getFilteredSpans).Methods(http.MethodPost)
router.HandleFunc("/api/v1/getFilteredSpans/aggregates", aH.getFilteredSpanAggregates).Methods(http.MethodGet) router.HandleFunc("/api/v1/getFilteredSpans/aggregates", aH.getFilteredSpanAggregates).Methods(http.MethodPost)
router.HandleFunc("/api/v1/errors", aH.getErrors).Methods(http.MethodGet) router.HandleFunc("/api/v1/errors", aH.getErrors).Methods(http.MethodGet)
router.HandleFunc("/api/v1/errorWithId", aH.getErrorForId).Methods(http.MethodGet) router.HandleFunc("/api/v1/errorWithId", aH.getErrorForId).Methods(http.MethodGet)
@ -978,7 +978,7 @@ func (aH *APIHandler) searchSpans(w http.ResponseWriter, r *http.Request) {
func (aH *APIHandler) getSpanFilters(w http.ResponseWriter, r *http.Request) { func (aH *APIHandler) getSpanFilters(w http.ResponseWriter, r *http.Request) {
query, err := parseSpanFilterRequest(r) query, err := parseSpanFilterRequestBody(r)
if aH.handleError(w, err, http.StatusBadRequest) { if aH.handleError(w, err, http.StatusBadRequest) {
return return
} }

View File

@ -482,183 +482,76 @@ func parseSpanSearchRequest(r *http.Request) (*model.SpanSearchParams, error) {
return params, nil return params, nil
} }
func parseSpanFilterRequest(r *http.Request) (*model.SpanFilterParams, error) { func parseSpanFilterRequestBody(r *http.Request) (*model.SpanFilterParams, error) {
var postData *model.SpanFilterParams
err := json.NewDecoder(r.Body).Decode(&postData)
startTime, err := parseTime("start", r)
if err != nil {
return nil, err
}
endTime, err := parseTimeMinusBuffer("end", r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
params := &model.SpanFilterParams{ postData.Start, err = parseTimeStr(postData.StartStr, "start")
Start: startTime, if err != nil {
End: endTime, return nil, err
ServiceName: []string{}, }
HttpRoute: []string{}, postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
HttpCode: []string{}, if err != nil {
HttpUrl: []string{}, return nil, err
HttpHost: []string{},
HttpMethod: []string{},
Component: []string{},
Status: []string{},
Operation: []string{},
GetFilters: []string{},
Exclude: []string{},
} }
params.ServiceName = fetchArrayValues("serviceName", r) return postData, nil
params.Status = fetchArrayValues("status", r)
params.Operation = fetchArrayValues("operation", r)
params.HttpCode = fetchArrayValues("httpCode", r)
params.HttpUrl = fetchArrayValues("httpUrl", r)
params.HttpHost = fetchArrayValues("httpHost", r)
params.HttpRoute = fetchArrayValues("httpRoute", r)
params.HttpMethod = fetchArrayValues("httpMethod", r)
params.Component = fetchArrayValues("component", r)
params.GetFilters = fetchArrayValues("getFilters", r)
params.Exclude = fetchArrayValues("exclude", r)
minDuration, err := parseTimestamp("minDuration", r)
if err == nil {
params.MinDuration = *minDuration
}
maxDuration, err := parseTimestamp("maxDuration", r)
if err == nil {
params.MaxDuration = *maxDuration
}
return params, nil
} }
func parseFilteredSpansRequest(r *http.Request) (*model.GetFilteredSpansParams, error) { func parseFilteredSpansRequest(r *http.Request) (*model.GetFilteredSpansParams, error) {
startTime, err := parseTime("start", r) var postData *model.GetFilteredSpansParams
if err != nil { err := json.NewDecoder(r.Body).Decode(&postData)
return nil, err
}
endTime, err := parseTimeMinusBuffer("end", r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
params := &model.GetFilteredSpansParams{ postData.Start, err = parseTimeStr(postData.StartStr, "start")
Start: startTime,
End: endTime,
ServiceName: []string{},
HttpRoute: []string{},
HttpCode: []string{},
HttpUrl: []string{},
HttpHost: []string{},
HttpMethod: []string{},
Component: []string{},
Status: []string{},
Operation: []string{},
Limit: 100,
Order: "descending",
Exclude: []string{},
}
params.ServiceName = fetchArrayValues("serviceName", r)
params.Status = fetchArrayValues("status", r)
params.Operation = fetchArrayValues("operation", r)
params.HttpCode = fetchArrayValues("httpCode", r)
params.HttpUrl = fetchArrayValues("httpUrl", r)
params.HttpHost = fetchArrayValues("httpHost", r)
params.HttpRoute = fetchArrayValues("httpRoute", r)
params.HttpMethod = fetchArrayValues("httpMethod", r)
params.Component = fetchArrayValues("component", r)
params.Exclude = fetchArrayValues("exclude", r)
limitStr := r.URL.Query().Get("limit")
if len(limitStr) != 0 {
limit, err := strconv.ParseInt(limitStr, 10, 64)
if err != nil {
return nil, errors.New("Limit param is not in correct format")
}
params.Limit = limit
} else {
params.Limit = 100
}
offsetStr := r.URL.Query().Get("offset")
if len(offsetStr) != 0 {
offset, err := strconv.ParseInt(offsetStr, 10, 64)
if err != nil {
return nil, errors.New("Offset param is not in correct format")
}
params.Offset = offset
}
tags, err := parseTagsV2("tags", r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(*tags) != 0 { postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
params.Tags = *tags if err != nil {
return nil, err
} }
minDuration, err := parseTimestamp("minDuration", r) if postData.Limit == 0 {
if err == nil { postData.Limit = 100
params.MinDuration = *minDuration
}
maxDuration, err := parseTimestamp("maxDuration", r)
if err == nil {
params.MaxDuration = *maxDuration
} }
kind := r.URL.Query().Get("kind") return postData, nil
if len(kind) != 0 {
params.Kind = kind
}
return params, nil
} }
func parseFilteredSpanAggregatesRequest(r *http.Request) (*model.GetFilteredSpanAggregatesParams, error) { func parseFilteredSpanAggregatesRequest(r *http.Request) (*model.GetFilteredSpanAggregatesParams, error) {
startTime, err := parseTime("start", r) var postData *model.GetFilteredSpanAggregatesParams
err := json.NewDecoder(r.Body).Decode(&postData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
endTime, err := parseTimeMinusBuffer("end", r) postData.Start, err = parseTimeStr(postData.StartStr, "start")
if err != nil {
return nil, err
}
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
if err != nil { if err != nil {
return nil, err return nil, err
} }
stepStr := r.URL.Query().Get("step") step := postData.StepSeconds
if len(stepStr) == 0 { if step == 0 {
return nil, errors.New("step param missing in query") return nil, errors.New("step param missing in query")
} }
stepInt, err := strconv.Atoi(stepStr) function := postData.Function
if err != nil {
return nil, errors.New("step param is not in correct format")
}
function := r.URL.Query().Get("function")
if len(function) == 0 { if len(function) == 0 {
return nil, errors.New("function param missing in query") return nil, errors.New("function param missing in query")
} else { } else {
@ -702,71 +595,17 @@ func parseFilteredSpanAggregatesRequest(r *http.Request) (*model.GetFilteredSpan
aggregationOption = "max" aggregationOption = "max"
} }
params := &model.GetFilteredSpanAggregatesParams{ postData.AggregationOption = aggregationOption
Start: startTime, postData.Dimension = dimension
End: endTime, // tags, err := parseTagsV2("tags", r)
ServiceName: []string{}, // if err != nil {
HttpRoute: []string{}, // return nil, err
HttpCode: []string{}, // }
HttpUrl: []string{}, // if len(*tags) != 0 {
HttpHost: []string{}, // params.Tags = *tags
HttpMethod: []string{}, // }
Component: []string{},
Status: []string{},
Operation: []string{},
StepSeconds: stepInt,
Dimension: dimension,
AggregationOption: aggregationOption,
Exclude: []string{},
}
params.ServiceName = fetchArrayValues("serviceName", r) return postData, nil
params.Status = fetchArrayValues("status", r)
params.Operation = fetchArrayValues("operation", r)
params.HttpCode = fetchArrayValues("httpCode", r)
params.HttpUrl = fetchArrayValues("httpUrl", r)
params.HttpHost = fetchArrayValues("httpHost", r)
params.HttpRoute = fetchArrayValues("httpRoute", r)
params.HttpMethod = fetchArrayValues("httpMethod", r)
params.Component = fetchArrayValues("component", r)
params.Exclude = fetchArrayValues("exclude", r)
tags, err := parseTagsV2("tags", r)
if err != nil {
return nil, err
}
if len(*tags) != 0 {
params.Tags = *tags
}
minDuration, err := parseTimestamp("minDuration", r)
if err == nil {
params.MinDuration = *minDuration
}
maxDuration, err := parseTimestamp("maxDuration", r)
if err == nil {
params.MaxDuration = *maxDuration
}
kind := r.URL.Query().Get("kind")
if len(kind) != 0 {
params.Kind = kind
}
groupBy := r.URL.Query().Get("groupBy")
if len(groupBy) != 0 {
params.GroupBy = groupBy
}
return params, nil
} }
func parseErrorRequest(r *http.Request) (*model.GetErrorParams, error) { func parseErrorRequest(r *http.Request) (*model.GetErrorParams, error) {
@ -792,61 +631,24 @@ func parseErrorRequest(r *http.Request) (*model.GetErrorParams, error) {
} }
func parseTagFilterRequest(r *http.Request) (*model.TagFilterParams, error) { func parseTagFilterRequest(r *http.Request) (*model.TagFilterParams, error) {
var postData *model.TagFilterParams
err := json.NewDecoder(r.Body).Decode(&postData)
startTime, err := parseTime("start", r)
if err != nil {
return nil, err
}
endTime, err := parseTimeMinusBuffer("end", r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
params := &model.TagFilterParams{ postData.Start, err = parseTimeStr(postData.StartStr, "start")
Start: startTime, if err != nil {
End: endTime, return nil, err
ServiceName: []string{}, }
HttpRoute: []string{}, postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
HttpCode: []string{}, if err != nil {
HttpUrl: []string{}, return nil, err
HttpHost: []string{},
HttpMethod: []string{},
Component: []string{},
Status: []string{},
Operation: []string{},
Exclude: []string{},
} }
params.ServiceName = fetchArrayValues("serviceName", r) return postData, nil
params.Status = fetchArrayValues("status", r)
params.Operation = fetchArrayValues("operation", r)
params.HttpCode = fetchArrayValues("httpCode", r)
params.HttpUrl = fetchArrayValues("httpUrl", r)
params.HttpHost = fetchArrayValues("httpHost", r)
params.HttpRoute = fetchArrayValues("httpRoute", r)
params.HttpMethod = fetchArrayValues("httpMethod", r)
params.Component = fetchArrayValues("component", r)
params.Exclude = fetchArrayValues("exclude", r)
minDuration, err := parseTimestamp("minDuration", r)
if err == nil {
params.MinDuration = *minDuration
}
maxDuration, err := parseTimestamp("maxDuration", r)
if err == nil {
params.MaxDuration = *maxDuration
}
return params, nil
} }
func parseErrorsRequest(r *http.Request) (*model.GetErrorsParams, error) { func parseErrorsRequest(r *http.Request) (*model.GetErrorsParams, error) {
@ -957,6 +759,45 @@ func parseApplicationPercentileRequest(r *http.Request) (*model.ApplicationPerce
} }
func parseTimeStr(timeStr string, param string) (*time.Time, error) {
if len(timeStr) == 0 {
return nil, fmt.Errorf("%s param missing in query", param)
}
timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
if err != nil || len(timeStr) == 0 {
return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
}
timeFmt := time.Unix(0, timeUnix)
return &timeFmt, nil
}
func parseTimeMinusBufferStr(timeStr string, param string) (*time.Time, error) {
if len(timeStr) == 0 {
return nil, fmt.Errorf("%s param missing in query", param)
}
timeUnix, err := strconv.ParseInt(timeStr, 10, 64)
if err != nil || len(timeStr) == 0 {
return nil, fmt.Errorf("%s param is not in correct timestamp format", param)
}
timeUnixNow := time.Now().UnixNano()
if timeUnix > timeUnixNow-30000000000 {
timeUnix = timeUnix - 30000000000
}
timeFmt := time.Unix(0, timeUnix)
return &timeFmt, nil
}
func parseTime(param string, r *http.Request) (*time.Time, error) { func parseTime(param string, r *http.Request) (*time.Time, error) {
timeStr := r.URL.Query().Get(param) timeStr := r.URL.Query().Get(param)

View File

@ -117,85 +117,94 @@ type SpanSearchParams struct {
} }
type GetFilteredSpansParams struct { type GetFilteredSpansParams struct {
ServiceName []string ServiceName []string `json:"serviceName"`
Operation []string Operation []string `json:"operation"`
Kind string Kind string `json:"kind"`
Status []string Status []string `json:"status"`
HttpRoute []string HttpRoute []string `json:"httpRoute"`
HttpCode []string HttpCode []string `json:"httpCode"`
HttpUrl []string HttpUrl []string `json:"httpUrl"`
HttpHost []string HttpHost []string `json:"httpHost"`
HttpMethod []string HttpMethod []string `json:"httpMethod"`
Component []string Component []string `json:"component"`
StartStr string `json:"start"`
EndStr string `json:"end"`
MinDuration string `json:"minDuration"`
MaxDuration string `json:"maxDuration"`
Limit int64 `json:"limit"`
Order string `json:"order"`
Offset int64 `json:"offset"`
Tags []TagQueryV2 `json:"tags"`
Exclude []string `json:"exclude"`
Start *time.Time Start *time.Time
End *time.Time End *time.Time
MinDuration string
MaxDuration string
Limit int64
Order string
Offset int64
Tags []TagQueryV2
Exclude []string
} }
type GetFilteredSpanAggregatesParams struct { type GetFilteredSpanAggregatesParams struct {
ServiceName []string ServiceName []string `json:"serviceName"`
Operation []string Operation []string `json:"operation"`
Kind string Kind string `json:"kind"`
Status []string Status []string `json:"status"`
HttpRoute []string HttpRoute []string `json:"httpRoute"`
HttpCode []string HttpCode []string `json:"httpCode"`
HttpUrl []string HttpUrl []string `json:"httpUrl"`
HttpHost []string HttpHost []string `json:"httpHost"`
HttpMethod []string HttpMethod []string `json:"httpMethod"`
Component []string Component []string `json:"component"`
MinDuration string MinDuration string `json:"minDuration"`
MaxDuration string MaxDuration string `json:"maxDuration"`
Tags []TagQueryV2 Tags []TagQueryV2 `json:"tags"`
StartStr string `json:"start"`
EndStr string `json:"end"`
StepSeconds int `json:"step"`
Dimension string `json:"dimension"`
AggregationOption string `json:"aggregationOption"`
GroupBy string `json:"groupBy"`
Function string `json:"function"`
Exclude []string `json:"exclude"`
Start *time.Time Start *time.Time
End *time.Time End *time.Time
StepSeconds int
Dimension string
AggregationOption string
GroupBy string
Function string
Exclude []string
} }
type SpanFilterParams struct { type SpanFilterParams struct {
Status []string Status []string `json:"status"`
ServiceName []string ServiceName []string `json:"serviceName"`
HttpRoute []string HttpRoute []string `json:"httpRoute"`
HttpCode []string HttpCode []string `json:"httpCode"`
HttpUrl []string HttpUrl []string `json:"httpUrl"`
HttpHost []string HttpHost []string `json:"httpHost"`
HttpMethod []string HttpMethod []string `json:"httpMethod"`
Component []string Component []string `json:"component"`
Operation []string Operation []string `json:"operation"`
GetFilters []string GetFilters []string `json:"getFilters"`
Exclude []string Exclude []string `json:"exclude"`
MinDuration string MinDuration string `json:"minDuration"`
MaxDuration string MaxDuration string `json:"maxDuration"`
StartStr string `json:"start"`
EndStr string `json:"end"`
Start *time.Time Start *time.Time
End *time.Time End *time.Time
} }
type TagFilterParams struct { type TagFilterParams struct {
Status []string Status []string `json:"status"`
ServiceName []string ServiceName []string `json:"serviceName"`
HttpRoute []string HttpRoute []string `json:"httpRoute"`
HttpCode []string HttpCode []string `json:"httpCode"`
HttpUrl []string HttpUrl []string `json:"httpUrl"`
HttpHost []string HttpHost []string `json:"httpHost"`
HttpMethod []string HttpMethod []string `json:"httpMethod"`
Component []string Component []string `json:"component"`
Operation []string Operation []string `json:"operation"`
Exclude []string Exclude []string `json:"exclude"`
MinDuration string MinDuration string `json:"minDuration"`
MaxDuration string MaxDuration string `json:"maxDuration"`
StartStr string `json:"start"`
EndStr string `json:"end"`
Start *time.Time Start *time.Time
End *time.Time End *time.Time
} }
type TTLParams struct { type TTLParams struct {
Type string Type string
Duration string Duration string