mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 10:20:29 -06:00
Loki: Apply scopes to expression if feature toggle is enabled. (#95917)
Signed-off-by: bergquist <carl.bergquist@gmail.com> Co-authored-by: Kyle Brandt <kyle@grafana.com>
This commit is contained in:
parent
8148f0c3bb
commit
c7b6822a5e
@ -162,6 +162,7 @@ export interface FeatureToggles {
|
||||
onPremToCloudMigrationsAlerts?: boolean;
|
||||
alertingSaveStatePeriodic?: boolean;
|
||||
promQLScope?: boolean;
|
||||
logQLScope?: boolean;
|
||||
sqlExpressions?: boolean;
|
||||
nodeGraphDotLayout?: boolean;
|
||||
groupToNestedTableTransformation?: boolean;
|
||||
|
@ -1086,6 +1086,13 @@ var (
|
||||
Owner: grafanaObservabilityMetricsSquad,
|
||||
Expression: "true",
|
||||
},
|
||||
{
|
||||
Name: "logQLScope",
|
||||
Description: "In-development feature that will allow injection of labels into loki queries.",
|
||||
Stage: FeatureStagePrivatePreview,
|
||||
Owner: grafanaObservabilityLogsSquad,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "sqlExpressions",
|
||||
Description: "Enables using SQL and DuckDB functions as Expressions.",
|
||||
|
@ -143,6 +143,7 @@ onPremToCloudMigrations,preview,@grafana/grafana-operator-experience-squad,false
|
||||
onPremToCloudMigrationsAlerts,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
alertingSaveStatePeriodic,privatePreview,@grafana/alerting-squad,false,false,false
|
||||
promQLScope,GA,@grafana/observability-metrics,false,false,false
|
||||
logQLScope,privatePreview,@grafana/observability-logs,false,false,false
|
||||
sqlExpressions,experimental,@grafana/grafana-app-platform-squad,false,false,false
|
||||
nodeGraphDotLayout,experimental,@grafana/observability-traces-and-profiling,false,false,true
|
||||
groupToNestedTableTransformation,GA,@grafana/dataviz-squad,false,false,true
|
||||
|
|
@ -583,6 +583,10 @@ const (
|
||||
// In-development feature that will allow injection of labels into prometheus queries.
|
||||
FlagPromQLScope = "promQLScope"
|
||||
|
||||
// FlagLogQLScope
|
||||
// In-development feature that will allow injection of labels into loki queries.
|
||||
FlagLogQLScope = "logQLScope"
|
||||
|
||||
// FlagSqlExpressions
|
||||
// Enables using SQL and DuckDB functions as Expressions.
|
||||
FlagSqlExpressions = "sqlExpressions"
|
||||
|
@ -1865,6 +1865,19 @@
|
||||
"frontend": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "logQLScope",
|
||||
"resourceVersion": "1730842404843",
|
||||
"creationTimestamp": "2024-11-05T21:33:24Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "In-development feature that will allow injection of labels into loki queries.",
|
||||
"stage": "privatePreview",
|
||||
"codeowner": "@grafana/observability-logs",
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "logRequestsInstrumentedAsUnknown",
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/promlib/models"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
ngalertmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
@ -70,8 +71,9 @@ type datasourceInfo struct {
|
||||
|
||||
type QueryJSONModel struct {
|
||||
dataquery.LokiDataQuery
|
||||
Direction *string `json:"direction,omitempty"`
|
||||
SupportingQueryType *string `json:"supportingQueryType"`
|
||||
Direction *string `json:"direction,omitempty"`
|
||||
SupportingQueryType *string `json:"supportingQueryType"`
|
||||
Scopes []models.ScopeFilter `json:"scopes"`
|
||||
}
|
||||
|
||||
type ResponseOpts struct {
|
||||
@ -168,7 +170,7 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
||||
s.applyHeaders(ctx, req)
|
||||
}
|
||||
|
||||
return queryData(ctx, req, dsInfo, responseOpts, s.tracer, logger, isFeatureEnabled(ctx, featuremgmt.FlagLokiRunQueriesInParallel), isFeatureEnabled(ctx, featuremgmt.FlagLokiStructuredMetadata))
|
||||
return queryData(ctx, req, dsInfo, responseOpts, s.tracer, logger, isFeatureEnabled(ctx, featuremgmt.FlagLokiRunQueriesInParallel), isFeatureEnabled(ctx, featuremgmt.FlagLokiStructuredMetadata), isFeatureEnabled(ctx, featuremgmt.FlagLogQLScope))
|
||||
}
|
||||
|
||||
func (s *Service) applyHeaders(ctx context.Context, req backend.ForwardHTTPHeaders) {
|
||||
@ -188,13 +190,13 @@ func (s *Service) applyHeaders(ctx context.Context, req backend.ForwardHTTPHeade
|
||||
}
|
||||
}
|
||||
|
||||
func queryData(ctx context.Context, req *backend.QueryDataRequest, dsInfo *datasourceInfo, responseOpts ResponseOpts, tracer tracing.Tracer, plog log.Logger, runInParallel bool, requestStructuredMetadata bool) (*backend.QueryDataResponse, error) {
|
||||
func queryData(ctx context.Context, req *backend.QueryDataRequest, dsInfo *datasourceInfo, responseOpts ResponseOpts, tracer tracing.Tracer, plog log.Logger, runInParallel bool, requestStructuredMetadata, logQLScopes bool) (*backend.QueryDataResponse, error) {
|
||||
result := backend.NewQueryDataResponse()
|
||||
|
||||
api := newLokiAPI(dsInfo.HTTPClient, dsInfo.URL, plog, tracer, requestStructuredMetadata)
|
||||
|
||||
start := time.Now()
|
||||
queries, err := parseQuery(req)
|
||||
queries, err := parseQuery(req, logQLScopes)
|
||||
if err != nil {
|
||||
plog.Error("Failed to prepare request to Loki", "error", err, "duration", time.Since(start), "queriesLength", len(queries), "stage", stagePrepareRequest)
|
||||
return result, err
|
||||
|
@ -125,7 +125,7 @@ func parseSupportingQueryType(jsonPointerValue *string) SupportingQueryType {
|
||||
}
|
||||
}
|
||||
|
||||
func parseQuery(queryContext *backend.QueryDataRequest) ([]*lokiQuery, error) {
|
||||
func parseQuery(queryContext *backend.QueryDataRequest, logqlScopesEnabled bool) ([]*lokiQuery, error) {
|
||||
qs := []*lokiQuery{}
|
||||
for _, query := range queryContext.Queries {
|
||||
model, err := parseQueryModel(query.JSON)
|
||||
@ -171,6 +171,13 @@ func parseQuery(queryContext *backend.QueryDataRequest) ([]*lokiQuery, error) {
|
||||
legendFormat = *model.LegendFormat
|
||||
}
|
||||
|
||||
if logqlScopesEnabled {
|
||||
rewrittenExpr, err := ApplyScopes(expr, model.Scopes)
|
||||
if err == nil {
|
||||
expr = rewrittenExpr
|
||||
}
|
||||
}
|
||||
|
||||
supportingQueryType := parseSupportingQueryType(model.SupportingQueryType)
|
||||
|
||||
qs = append(qs, &lokiQuery{
|
||||
@ -184,6 +191,7 @@ func parseQuery(queryContext *backend.QueryDataRequest) ([]*lokiQuery, error) {
|
||||
End: end,
|
||||
RefID: query.RefID,
|
||||
SupportingQueryType: supportingQueryType,
|
||||
Scopes: model.Scopes,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func TestParseQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
models, err := parseQuery(queryContext)
|
||||
models, err := parseQuery(queryContext, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*15, models[0].Step)
|
||||
require.Equal(t, "go_goroutines 15s 15000 3000s 3000 3000000", models[0].Expr)
|
||||
@ -57,7 +57,7 @@ func TestParseQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
models, err := parseQuery(queryContext)
|
||||
models, err := parseQuery(queryContext, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, SupportingQueryLogsVolume, models[0].SupportingQueryType)
|
||||
})
|
||||
@ -83,7 +83,7 @@ func TestParseQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
models, err := parseQuery(queryContext)
|
||||
models, err := parseQuery(queryContext, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, SupportingQueryType("foo"), models[0].SupportingQueryType)
|
||||
})
|
||||
@ -109,11 +109,42 @@ func TestParseQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
models, err := parseQuery(queryContext)
|
||||
models, err := parseQuery(queryContext, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, SupportingQueryNone, models[0].SupportingQueryType)
|
||||
})
|
||||
|
||||
t.Run("parsing query model with scopes", func(t *testing.T) {
|
||||
queryContext := &backend.QueryDataRequest{
|
||||
Queries: []backend.DataQuery{
|
||||
{
|
||||
JSON: []byte(`
|
||||
{
|
||||
"expr": "{} |= \"problems\"",
|
||||
"format": "time_series",
|
||||
"refId": "A",
|
||||
"scopes": [{"key": "namespace", "value": "logish", "operator": "equals"}]
|
||||
}`,
|
||||
),
|
||||
TimeRange: backend.TimeRange{
|
||||
From: time.Now().Add(-3000 * time.Second),
|
||||
To: time.Now(),
|
||||
},
|
||||
Interval: time.Second * 15,
|
||||
MaxDataPoints: 200,
|
||||
},
|
||||
},
|
||||
}
|
||||
models, err := parseQuery(queryContext, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Second*15, models[0].Step)
|
||||
require.Equal(t, 1, len(models[0].Scopes))
|
||||
require.Equal(t, "namespace", models[0].Scopes[0].Key)
|
||||
require.Equal(t, "logish", models[0].Scopes[0].Value)
|
||||
require.Equal(t, "equals", string(models[0].Scopes[0].Operator))
|
||||
require.Equal(t, `{namespace="logish"} |= "problems"`, models[0].Expr)
|
||||
})
|
||||
|
||||
t.Run("interpolate variables, range between 1s and 0.5s", func(t *testing.T) {
|
||||
expr := "go_goroutines $__interval $__interval_ms $__range $__range_s $__range_ms"
|
||||
queryType := dataquery.LokiQueryTypeRange
|
||||
|
@ -3,6 +3,7 @@ package loki
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/promlib/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb/loki/kinds/dataquery"
|
||||
)
|
||||
|
||||
@ -39,4 +40,5 @@ type lokiQuery struct {
|
||||
End time.Time
|
||||
RefID string
|
||||
SupportingQueryType SupportingQueryType
|
||||
Scopes []models.ScopeFilter
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user