mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-10 04:49:02 +08:00
fix: add missed variable substitution for promql querires (#3584)
This commit is contained in:
parent
b0861f4fe0
commit
735ab8e118
@ -497,7 +497,7 @@ func PromFormattedValue(v interface{}) string {
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", x)
|
||||
case string:
|
||||
return fmt.Sprintf("%s", x)
|
||||
return x
|
||||
case bool:
|
||||
return fmt.Sprintf("%v", x)
|
||||
case []interface{}:
|
||||
@ -506,7 +506,12 @@ func PromFormattedValue(v interface{}) string {
|
||||
}
|
||||
switch x[0].(type) {
|
||||
case string, int, float32, float64, bool:
|
||||
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(x)), "|"), "[]")
|
||||
// list of values joined by | for promql - a value can contain whitespace
|
||||
var str []string
|
||||
for _, sVal := range x {
|
||||
str = append(str, fmt.Sprintf("%v", sVal))
|
||||
}
|
||||
return strings.Join(str, "|")
|
||||
default:
|
||||
zap.L().Error("invalid type for prom formatted value", zap.Any("type", reflect.TypeOf(x[0])))
|
||||
return ""
|
||||
|
@ -1044,5 +1044,29 @@ func ParseQueryRangeParams(r *http.Request) (*v3.QueryRangeParamsV3, *model.ApiE
|
||||
}
|
||||
}
|
||||
|
||||
// replace go template variables in prometheus query
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypePromQL {
|
||||
for _, promQuery := range queryRangeParams.CompositeQuery.PromQueries {
|
||||
if promQuery.Disabled {
|
||||
continue
|
||||
}
|
||||
tmpl := template.New("prometheus-query")
|
||||
tmpl, err := tmpl.Parse(promQuery.Query)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
}
|
||||
var query bytes.Buffer
|
||||
|
||||
// replace go template variables
|
||||
querytemplate.AssignReservedVarsV3(queryRangeParams)
|
||||
|
||||
err = tmpl.Execute(&query, queryRangeParams.Variables)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
}
|
||||
promQuery.Query = query.String()
|
||||
}
|
||||
}
|
||||
|
||||
return queryRangeParams, nil
|
||||
}
|
||||
|
@ -757,3 +757,108 @@ func TestParseQueryRangeParamsDashboardVarsSubstitution(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseQueryRangeParamsPromQLVars(t *testing.T) {
|
||||
reqCases := []struct {
|
||||
desc string
|
||||
compositeQuery v3.CompositeQuery
|
||||
variables map[string]interface{}
|
||||
expectErr bool
|
||||
errMsg string
|
||||
expectedQuery string
|
||||
}{
|
||||
{
|
||||
desc: "valid prom query with dashboard variables",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: v3.QueryTypePromQL,
|
||||
PromQueries: map[string]*v3.PromQuery{
|
||||
"A": {
|
||||
Query: "http_calls_total{service_name=\"{{.service_name}}\", operation_name=~\"{{.operation_name}}\"}",
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
variables: map[string]interface{}{
|
||||
"service_name": "route",
|
||||
"operation_name": []interface{}{
|
||||
"GET /route",
|
||||
"POST /route",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
expectedQuery: "http_calls_total{service_name=\"route\", operation_name=~\"GET /route|POST /route\"}",
|
||||
},
|
||||
{
|
||||
desc: "valid prom query with dashboard variables",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: v3.QueryTypePromQL,
|
||||
PromQueries: map[string]*v3.PromQuery{
|
||||
"A": {
|
||||
Query: "http_calls_total{service_name=\"{{.service_name}}\", status_code=~\"{{.status_code}}\"}",
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
variables: map[string]interface{}{
|
||||
"service_name": "route",
|
||||
"status_code": []interface{}{
|
||||
200,
|
||||
505,
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
expectedQuery: "http_calls_total{service_name=\"route\", status_code=~\"200|505\"}",
|
||||
},
|
||||
{
|
||||
desc: "valid prom query with dashboard variables",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: v3.QueryTypePromQL,
|
||||
PromQueries: map[string]*v3.PromQuery{
|
||||
"A": {
|
||||
Query: "http_calls_total{service_name=\"{{.service_name}}\", quantity=~\"{{.quantity}}\"}",
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
variables: map[string]interface{}{
|
||||
"service_name": "route",
|
||||
"quantity": []interface{}{
|
||||
4.5,
|
||||
4.6,
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
expectedQuery: "http_calls_total{service_name=\"route\", quantity=~\"4.5|4.6\"}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range reqCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
|
||||
queryRangeParams := &v3.QueryRangeParamsV3{
|
||||
Start: time.Now().Add(-time.Hour).UnixMilli(),
|
||||
End: time.Now().UnixMilli(),
|
||||
Step: time.Minute.Microseconds(),
|
||||
CompositeQuery: &tc.compositeQuery,
|
||||
Variables: tc.variables,
|
||||
}
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
err := json.NewEncoder(body).Encode(queryRangeParams)
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v3/query_range", body)
|
||||
|
||||
parsedQueryRangeParams, apiErr := ParseQueryRangeParams(req)
|
||||
if tc.expectErr {
|
||||
require.Error(t, apiErr)
|
||||
require.Contains(t, apiErr.Error(), tc.errMsg)
|
||||
} else {
|
||||
require.Nil(t, apiErr)
|
||||
require.Equal(t, parsedQueryRangeParams.CompositeQuery.PromQueries["A"].Query, tc.expectedQuery)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user