diff --git a/pkg/expr/nodes.go b/pkg/expr/nodes.go index b63e6882280..21eb7c763b4 100644 --- a/pkg/expr/nodes.go +++ b/pkg/expr/nodes.go @@ -202,6 +202,7 @@ func (s *Service) buildDSNode(dp *simple.DirectedGraph, rn *rawNode, req *Reques // other nodes they must have already been executed and their results must // already by in vars. func (dn *DSNode) Execute(ctx context.Context, now time.Time, _ mathexp.Vars, s *Service) (mathexp.Results, error) { + logger := logger.FromContext(ctx).New("datasourceType", dn.datasource.Type) dsInstanceSettings, err := adapters.ModelToInstanceSettings(dn.datasource, s.decryptSecureJsonDataFn(ctx)) if err != nil { return mathexp.Results{}, fmt.Errorf("%v: %w", "failed to convert datasource instance settings", err) diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index 03106861354..aea2a7a5c56 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -92,7 +92,7 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) { ac: api.AccessControl, } - evaluator := eval.NewEvaluator(api.Cfg, log.New("ngalert.eval"), api.DatasourceCache, api.ExpressionService) + evaluator := eval.NewEvaluator(api.Cfg, api.DatasourceCache, api.ExpressionService) // Register endpoints for proxying to Alertmanager-compatible backends. api.RegisterAlertmanagerApiEndpoints(NewForkingAM( diff --git a/pkg/services/ngalert/eval/eval.go b/pkg/services/ngalert/eval/eval.go index 24e83b6e6b5..0578b5f047f 100644 --- a/pkg/services/ngalert/eval/eval.go +++ b/pkg/services/ngalert/eval/eval.go @@ -22,6 +22,8 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/data" ) +var logger = log.New("ngalert.eval") + //go:generate mockery --name Evaluator --structname FakeEvaluator --inpackage --filename evaluator_mock.go --with-expecter type Evaluator interface { // ConditionEval executes conditions and evaluates the result. @@ -34,19 +36,16 @@ type Evaluator interface { type evaluatorImpl struct { cfg *setting.Cfg - log log.Logger dataSourceCache datasources.CacheService expressionService *expr.Service } func NewEvaluator( cfg *setting.Cfg, - log log.Logger, datasourceCache datasources.CacheService, expressionService *expr.Service) Evaluator { return &evaluatorImpl{ cfg: cfg, - log: log, dataSourceCache: datasourceCache, expressionService: expressionService, } @@ -313,10 +312,10 @@ func queryDataResponseToExecutionResults(c models.Condition, execResp *backend.Q return result } -func executeQueriesAndExpressions(ctx EvaluationContext, data []models.AlertQuery, exprService *expr.Service, dsCacheService datasources.CacheService, log log.Logger) (resp *backend.QueryDataResponse, err error) { +func executeQueriesAndExpressions(ctx EvaluationContext, data []models.AlertQuery, exprService *expr.Service, dsCacheService datasources.CacheService) (resp *backend.QueryDataResponse, err error) { defer func() { if e := recover(); e != nil { - log.Error("alert rule panic", "error", e, "stack", string(debug.Stack())) + logger.FromContext(ctx.Ctx).Error("alert rule panic", "error", e, "stack", string(debug.Stack())) panicErr := fmt.Errorf("alert rule panic; please check the logs for the full stack") if err != nil { err = fmt.Errorf("queries and expressions execution failed: %w; %v", err, panicErr.Error()) @@ -578,7 +577,7 @@ func (e *evaluatorImpl) QueriesAndExpressionsEval(ctx EvaluationContext, data [] timeoutCtx, cancelFn := ctx.WithTimeout(e.cfg.UnifiedAlerting.EvaluationTimeout) defer cancelFn() - execResult, err := executeQueriesAndExpressions(timeoutCtx, data, e.expressionService, e.dataSourceCache, e.log) + execResult, err := executeQueriesAndExpressions(timeoutCtx, data, e.expressionService, e.dataSourceCache) if err != nil { return nil, fmt.Errorf("failed to execute conditions: %w", err) } diff --git a/pkg/services/ngalert/eval/eval_test.go b/pkg/services/ngalert/eval/eval_test.go index 2f1f792c8af..e38e9c3bffd 100644 --- a/pkg/services/ngalert/eval/eval_test.go +++ b/pkg/services/ngalert/eval/eval_test.go @@ -12,7 +12,6 @@ import ( ptr "github.com/xorcare/pointer" "github.com/grafana/grafana/pkg/expr" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasources" fakes "github.com/grafana/grafana/pkg/services/datasources/fakes" "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -444,7 +443,7 @@ func TestValidate(t *testing.T) { cacheService := &fakes.FakeCacheService{} condition := testCase.condition(cacheService) - evaluator := NewEvaluator(&setting.Cfg{ExpressionsEnabled: true}, log.New("test"), cacheService, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil)) + evaluator := NewEvaluator(&setting.Cfg{ExpressionsEnabled: true}, cacheService, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil)) evalCtx := Context(context.Background(), u) err := evaluator.Validate(evalCtx, condition) diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index d540204c157..1e8abde2fdb 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -1,6 +1,7 @@ package models import ( + "context" "encoding/json" "errors" "fmt" @@ -457,3 +458,14 @@ func (g RulesGroup) SortByGroupIndex() { return g[i].RuleGroupIndex < g[j].RuleGroupIndex }) } + +type ruleKeyContextKey struct{} + +func WithRuleKey(ctx context.Context, ruleKey AlertRuleKey) context.Context { + return context.WithValue(ctx, ruleKeyContextKey{}, ruleKey) +} + +func RuleKeyFromContext(ctx context.Context) (AlertRuleKey, bool) { + key, ok := ctx.Value(ruleKeyContextKey{}).(AlertRuleKey) + return key, ok +} diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index d851b112777..9e38ba31e06 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -183,7 +183,7 @@ func (ng *AlertNG) init() error { schedCfg := schedule.SchedulerCfg{ Cfg: ng.Cfg.UnifiedAlerting, C: clk, - Evaluator: eval.NewEvaluator(ng.Cfg, ng.Log, ng.DataSourceCache, ng.ExpressionService), + Evaluator: eval.NewEvaluator(ng.Cfg, ng.DataSourceCache, ng.ExpressionService), RuleStore: store, Metrics: ng.Metrics.GetSchedulerMetrics(), AlertSender: alertsRouter, @@ -236,6 +236,14 @@ func (ng *AlertNG) init() error { } api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics()) + log.RegisterContextualLogProvider(func(ctx context.Context) ([]interface{}, bool) { + key, ok := models.RuleKeyFromContext(ctx) + if !ok { + return nil, false + } + return key.LogContext(), true + }) + return DeclareFixedRoles(ng.accesscontrolService) } diff --git a/pkg/services/ngalert/schedule/schedule.go b/pkg/services/ngalert/schedule/schedule.go index 6325955221a..8efb9608f5c 100644 --- a/pkg/services/ngalert/schedule/schedule.go +++ b/pkg/services/ngalert/schedule/schedule.go @@ -306,7 +306,8 @@ func (sch *schedule) schedulePeriodic(ctx context.Context, t *ticker.T) error { } func (sch *schedule) ruleRoutine(grafanaCtx context.Context, key ngmodels.AlertRuleKey, evalCh <-chan *evaluation, updateCh <-chan ruleVersion) error { - logger := sch.log.New(key.LogContext()...) + grafanaCtx = ngmodels.WithRuleKey(grafanaCtx, key) + logger := sch.log.FromContext(grafanaCtx) logger.Debug("Alert rule routine started") orgID := fmt.Sprint(key.OrgID) diff --git a/pkg/services/ngalert/schedule/schedule_unit_test.go b/pkg/services/ngalert/schedule/schedule_unit_test.go index fcb983ea8fa..70f6051ba93 100644 --- a/pkg/services/ngalert/schedule/schedule_unit_test.go +++ b/pkg/services/ngalert/schedule/schedule_unit_test.go @@ -20,7 +20,6 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/expr" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/image" @@ -482,7 +481,6 @@ func setupScheduler(t *testing.T, rs *fakeRulesStore, is *state.FakeInstanceStor t.Helper() mockedClock := clock.NewMock() - logger := log.New("ngalert schedule test") if rs == nil { rs = newFakeRulesStore() @@ -494,7 +492,7 @@ func setupScheduler(t *testing.T, rs *fakeRulesStore, is *state.FakeInstanceStor var evaluator eval.Evaluator = evalMock if evalMock == nil { - evaluator = eval.NewEvaluator(&setting.Cfg{ExpressionsEnabled: true}, logger, nil, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil)) + evaluator = eval.NewEvaluator(&setting.Cfg{ExpressionsEnabled: true}, nil, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil)) } if registry == nil { diff --git a/pkg/services/ngalert/state/manager.go b/pkg/services/ngalert/state/manager.go index 066c8c0fa22..c973bc734fc 100644 --- a/pkg/services/ngalert/state/manager.go +++ b/pkg/services/ngalert/state/manager.go @@ -162,7 +162,7 @@ func (st *Manager) ResetStateByRuleUID(ctx context.Context, ruleKey ngModels.Ale // ProcessEvalResults updates the current states that belong to a rule with the evaluation results. // if extraLabels is not empty, those labels will be added to every state. The extraLabels take precedence over rule labels and result labels func (st *Manager) ProcessEvalResults(ctx context.Context, evaluatedAt time.Time, alertRule *ngModels.AlertRule, results eval.Results, extraLabels data.Labels) []*State { - logger := st.log.New(alertRule.GetKey().LogContext()...) + logger := st.log.FromContext(ctx) logger.Debug("State manager processing evaluation results", "resultCount", len(results)) var states []*State processedResults := make(map[string]*State, len(results))