mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Improve logs in state manager and historian (#57374)
* Touch up log statements, fix casing, add and normalize contexts * Dedicated logger for dashboard resolver * Avoid injecting logger to historian * More minor log touch-ups * Dedicated logger for state manager * Use rule context in annotation creator * Rename base logger and avoid redundant contextual loggers
This commit is contained in:
@@ -189,8 +189,8 @@ func (ng *AlertNG) init() error {
|
|||||||
AlertSender: alertsRouter,
|
AlertSender: alertsRouter,
|
||||||
}
|
}
|
||||||
|
|
||||||
historian := historian.NewAnnotationHistorian(ng.annotationsRepo, ng.dashboardService, ng.Log)
|
historian := historian.NewAnnotationHistorian(ng.annotationsRepo, ng.dashboardService)
|
||||||
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store, ng.imageService, clk, historian)
|
stateManager := state.NewManager(ng.Metrics.GetStateMetrics(), appUrl, store, store, ng.imageService, clk, historian)
|
||||||
scheduler := schedule.NewScheduler(schedCfg, appUrl, stateManager)
|
scheduler := schedule.NewScheduler(schedCfg, appUrl, stateManager)
|
||||||
|
|
||||||
// if it is required to include folder title to the alerts, we need to subscribe to changes of alert title
|
// if it is required to include folder title to the alerts, we need to subscribe to changes of alert title
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
@@ -107,7 +106,7 @@ func TestWarmStateCache(t *testing.T) {
|
|||||||
Labels: labels,
|
Labels: labels,
|
||||||
}
|
}
|
||||||
_ = dbstore.SaveAlertInstances(ctx, instance2)
|
_ = dbstore.SaveAlertInstances(ctx, instance2)
|
||||||
st := state.NewManager(log.New("test"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.NewMock(), &state.FakeHistorian{})
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.NewMock(), &state.FakeHistorian{})
|
||||||
st.Warm(ctx)
|
st.Warm(ctx)
|
||||||
|
|
||||||
t.Run("instance cache has expected entries", func(t *testing.T) {
|
t.Run("instance cache has expected entries", func(t *testing.T) {
|
||||||
@@ -158,7 +157,7 @@ func TestAlertingTicker(t *testing.T) {
|
|||||||
Metrics: testMetrics.GetSchedulerMetrics(),
|
Metrics: testMetrics.GetSchedulerMetrics(),
|
||||||
AlertSender: notifier,
|
AlertSender: notifier,
|
||||||
}
|
}
|
||||||
st := state.NewManager(log.New("test"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.NewMock(), &state.FakeHistorian{})
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.NewMock(), &state.FakeHistorian{})
|
||||||
appUrl := &url.URL{
|
appUrl := &url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ func setupScheduler(t *testing.T, rs *fakeRulesStore, is *state.FakeInstanceStor
|
|||||||
}
|
}
|
||||||
|
|
||||||
stateRs := state.FakeRuleReader{}
|
stateRs := state.FakeRuleReader{}
|
||||||
st := state.NewManager(log.New("test"), m.GetStateMetrics(), nil, &stateRs, is, &image.NoopImageService{}, mockedClock, &state.FakeHistorian{})
|
st := state.NewManager(m.GetStateMetrics(), nil, &stateRs, is, &image.NoopImageService{}, mockedClock, &state.FakeHistorian{})
|
||||||
return NewScheduler(schedCfg, appUrl, st)
|
return NewScheduler(schedCfg, appUrl, st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,13 +90,13 @@ func (rs *ruleStates) getOrCreate(ctx context.Context, log log.Logger, alertRule
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(dupes) > 0 {
|
if len(dupes) > 0 {
|
||||||
log.Warn("evaluation result contains either reserved labels or labels declared in the rules. Those labels from the result will be ignored", "labels", dupes)
|
log.Warn("Evaluation result contains either reserved labels or labels declared in the rules. Those labels from the result will be ignored", "labels", dupes)
|
||||||
}
|
}
|
||||||
|
|
||||||
il := ngModels.InstanceLabels(lbs)
|
il := ngModels.InstanceLabels(lbs)
|
||||||
id, err := il.StringKey()
|
id, err := il.StringKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error getting cacheId for entry", "error", err.Error())
|
log.Error("Error getting cacheId for entry", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if state, ok := rs.states[id]; ok {
|
if state, ok := rs.states[id]; ok {
|
||||||
@@ -145,7 +145,7 @@ func (rs *ruleStates) expandRuleLabelsAndAnnotations(ctx context.Context, log lo
|
|||||||
ev, err := expandTemplate(ctx, alertRule.Title, v, templateLabels, alertInstance, externalURL)
|
ev, err := expandTemplate(ctx, alertRule.Title, v, templateLabels, alertInstance, externalURL)
|
||||||
expanded[k] = ev
|
expanded[k] = ev
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error in expanding template", "name", k, "value", v, "error", err.Error())
|
log.Error("Error in expanding template", "name", k, "value", v, "error", err)
|
||||||
// Store the original template on error.
|
// Store the original template on error.
|
||||||
expanded[k] = v
|
expanded[k] = v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,17 @@ type AnnotationStateHistorian struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAnnotationHistorian(annotations annotations.Repository, dashboards dashboards.DashboardService, log log.Logger) *AnnotationStateHistorian {
|
func NewAnnotationHistorian(annotations annotations.Repository, dashboards dashboards.DashboardService) *AnnotationStateHistorian {
|
||||||
return &AnnotationStateHistorian{
|
return &AnnotationStateHistorian{
|
||||||
annotations: annotations,
|
annotations: annotations,
|
||||||
dashboards: newDashboardResolver(dashboards, log, defaultDashboardCacheExpiry),
|
dashboards: newDashboardResolver(dashboards, defaultDashboardCacheExpiry),
|
||||||
log: log,
|
log: log.New("ngalert.state.historian"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *AnnotationStateHistorian) RecordState(ctx context.Context, rule *ngmodels.AlertRule, labels data.Labels, evaluatedAt time.Time, currentData, previousData state.InstanceStateAndReason) {
|
func (h *AnnotationStateHistorian) RecordState(ctx context.Context, rule *ngmodels.AlertRule, labels data.Labels, evaluatedAt time.Time, currentData, previousData state.InstanceStateAndReason) {
|
||||||
h.log.Debug("alert state changed creating annotation", "alertRuleUID", rule.UID, "newState", currentData.String(), "oldState", previousData.String())
|
logger := h.log.New(rule.GetKey().LogContext()...)
|
||||||
|
logger.Debug("Alert state changed creating annotation", "newState", currentData.String(), "oldState", previousData.String())
|
||||||
|
|
||||||
labels = removePrivateLabels(labels)
|
labels = removePrivateLabels(labels)
|
||||||
annotationText := fmt.Sprintf("%s {%s} - %s", rule.Title, labels.String(), currentData.String())
|
annotationText := fmt.Sprintf("%s {%s} - %s", rule.Title, labels.String(), currentData.String())
|
||||||
@@ -51,13 +52,13 @@ func (h *AnnotationStateHistorian) RecordState(ctx context.Context, rule *ngmode
|
|||||||
|
|
||||||
panelId, err := strconv.ParseInt(panelUid, 10, 64)
|
panelId, err := strconv.ParseInt(panelUid, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log.Error("error parsing panelUID for alert annotation", "panelUID", panelUid, "alertRuleUID", rule.UID, "error", err.Error())
|
logger.Error("Error parsing panelUID for alert annotation", "panelUID", panelUid, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dashID, err := h.dashboards.getID(ctx, rule.OrgID, dashUid)
|
dashID, err := h.dashboards.getID(ctx, rule.OrgID, dashUid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log.Error("error getting dashboard for alert annotation", "dashboardUID", dashUid, "alertRuleUID", rule.UID, "error", err.Error())
|
logger.Error("Error getting dashboard for alert annotation", "dashboardUID", dashUid, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ func (h *AnnotationStateHistorian) RecordState(ctx context.Context, rule *ngmode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := h.annotations.Save(ctx, item); err != nil {
|
if err := h.annotations.Save(ctx, item); err != nil {
|
||||||
h.log.Error("error saving alert annotation", "alertRuleUID", rule.UID, "error", err.Error())
|
logger.Error("Error saving alert annotation", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ type dashboardResolver struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDashboardResolver(dbs dashboards.DashboardService, log log.Logger, expiry time.Duration) *dashboardResolver {
|
func newDashboardResolver(dbs dashboards.DashboardService, expiry time.Duration) *dashboardResolver {
|
||||||
return &dashboardResolver{
|
return &dashboardResolver{
|
||||||
dashboards: dbs,
|
dashboards: dbs,
|
||||||
cache: cache.New(expiry, maxDuration(2*expiry, minCleanupInterval)),
|
cache: cache.New(expiry, maxDuration(2*expiry, minCleanupInterval)),
|
||||||
singleflight: singleflight.Group{},
|
singleflight: singleflight.Group{},
|
||||||
log: log,
|
log: log.New("ngalert.dashboard-resolver"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ func (r *dashboardResolver) getID(ctx context.Context, orgID int64, uid string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
id, err, _ := r.singleflight.Do(key, func() (interface{}, error) {
|
id, err, _ := r.singleflight.Do(key, func() (interface{}, error) {
|
||||||
r.log.Debug("dashboard cache miss, querying dashboards", "dashboardUID", uid)
|
r.log.Debug("Dashboard cache miss, querying dashboards", "dashboardUID", uid)
|
||||||
|
|
||||||
var result interface{}
|
var result interface{}
|
||||||
query := &models.GetDashboardQuery{
|
query := &models.GetDashboardQuery{
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@@ -42,5 +41,5 @@ func TestDashboardResolver(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createDashboardResolverSut(dbs *dashboards.FakeDashboardService) *dashboardResolver {
|
func createDashboardResolverSut(dbs *dashboards.FakeDashboardService) *dashboardResolver {
|
||||||
return newDashboardResolver(dbs, log.NewNopLogger(), 1*time.Nanosecond)
|
return newDashboardResolver(dbs, 1*time.Nanosecond)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ type Manager struct {
|
|||||||
externalURL *url.URL
|
externalURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(logger log.Logger, metrics *metrics.State, externalURL *url.URL,
|
func NewManager(metrics *metrics.State, externalURL *url.URL,
|
||||||
ruleStore RuleReader, instanceStore InstanceStore, imageService image.ImageService, clock clock.Clock, historian Historian) *Manager {
|
ruleStore RuleReader, instanceStore InstanceStore, imageService image.ImageService, clock clock.Clock, historian Historian) *Manager {
|
||||||
manager := &Manager{
|
manager := &Manager{
|
||||||
cache: newCache(),
|
cache: newCache(),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
ResendDelay: ResendDelay, // TODO: make this configurable
|
ResendDelay: ResendDelay, // TODO: make this configurable
|
||||||
log: logger,
|
log: log.New("ngalert.state.manager"),
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
ruleStore: ruleStore,
|
ruleStore: ruleStore,
|
||||||
instanceStore: instanceStore,
|
instanceStore: instanceStore,
|
||||||
@@ -72,7 +72,7 @@ func (st *Manager) Warm(ctx context.Context) {
|
|||||||
|
|
||||||
orgIds, err := st.instanceStore.FetchOrgIds(ctx)
|
orgIds, err := st.instanceStore.FetchOrgIds(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st.log.Error("unable to fetch orgIds", "error", err.Error())
|
st.log.Error("Unable to fetch orgIds", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statesCount := 0
|
statesCount := 0
|
||||||
@@ -83,7 +83,7 @@ func (st *Manager) Warm(ctx context.Context) {
|
|||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
}
|
}
|
||||||
if err := st.ruleStore.ListAlertRules(ctx, &ruleCmd); err != nil {
|
if err := st.ruleStore.ListAlertRules(ctx, &ruleCmd); err != nil {
|
||||||
st.log.Error("unable to fetch previous state", "msg", err.Error())
|
st.log.Error("Unable to fetch previous state", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleByUID := make(map[string]*ngModels.AlertRule, len(ruleCmd.Result))
|
ruleByUID := make(map[string]*ngModels.AlertRule, len(ruleCmd.Result))
|
||||||
@@ -99,7 +99,7 @@ func (st *Manager) Warm(ctx context.Context) {
|
|||||||
RuleOrgID: orgId,
|
RuleOrgID: orgId,
|
||||||
}
|
}
|
||||||
if err := st.instanceStore.ListAlertInstances(ctx, &cmd); err != nil {
|
if err := st.instanceStore.ListAlertInstances(ctx, &cmd); err != nil {
|
||||||
st.log.Error("unable to fetch previous state", "msg", err.Error())
|
st.log.Error("Unable to fetch previous state", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range cmd.Result {
|
for _, entry := range cmd.Result {
|
||||||
@@ -118,7 +118,7 @@ func (st *Manager) Warm(ctx context.Context) {
|
|||||||
lbs := map[string]string(entry.Labels)
|
lbs := map[string]string(entry.Labels)
|
||||||
cacheID, err := entry.Labels.StringKey()
|
cacheID, err := entry.Labels.StringKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st.log.Error("error getting cacheId for entry", "msg", err.Error())
|
st.log.Error("Error getting cacheId for entry", "error", err)
|
||||||
}
|
}
|
||||||
rulesStates.states[cacheID] = &State{
|
rulesStates.states[cacheID] = &State{
|
||||||
AlertRuleUID: entry.RuleUID,
|
AlertRuleUID: entry.RuleUID,
|
||||||
@@ -137,7 +137,7 @@ func (st *Manager) Warm(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
st.cache.setAllStates(states)
|
st.cache.setAllStates(states)
|
||||||
st.log.Info("State cache has been initialized", "loaded_states", statesCount, "duration", time.Since(startTime))
|
st.log.Info("State cache has been initialized", "states", statesCount, "duration", time.Since(startTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Manager) Get(orgID int64, alertRuleUID, stateId string) *State {
|
func (st *Manager) Get(orgID int64, alertRuleUID, stateId string) *State {
|
||||||
@@ -147,15 +147,15 @@ func (st *Manager) Get(orgID int64, alertRuleUID, stateId string) *State {
|
|||||||
// ResetStateByRuleUID deletes all entries in the state manager that match the given rule UID.
|
// ResetStateByRuleUID deletes all entries in the state manager that match the given rule UID.
|
||||||
func (st *Manager) ResetStateByRuleUID(ctx context.Context, ruleKey ngModels.AlertRuleKey) []*State {
|
func (st *Manager) ResetStateByRuleUID(ctx context.Context, ruleKey ngModels.AlertRuleKey) []*State {
|
||||||
logger := st.log.New(ruleKey.LogContext()...)
|
logger := st.log.New(ruleKey.LogContext()...)
|
||||||
logger.Debug("resetting state of the rule")
|
logger.Debug("Resetting state of the rule")
|
||||||
states := st.cache.removeByRuleUID(ruleKey.OrgID, ruleKey.UID)
|
states := st.cache.removeByRuleUID(ruleKey.OrgID, ruleKey.UID)
|
||||||
if len(states) > 0 {
|
if len(states) > 0 {
|
||||||
err := st.instanceStore.DeleteAlertInstancesByRule(ctx, ruleKey)
|
err := st.instanceStore.DeleteAlertInstancesByRule(ctx, ruleKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to delete states that belong to a rule from database", ruleKey.LogContext()...)
|
logger.Error("Failed to delete states that belong to a rule from database", "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.Info("rules state was reset", "deleted_states", len(states))
|
logger.Info("Rules state was reset", "states", len(states))
|
||||||
return states
|
return states
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,17 +163,17 @@ func (st *Manager) ResetStateByRuleUID(ctx context.Context, ruleKey ngModels.Ale
|
|||||||
// if extraLabels is not empty, those labels will be added to every state. The extraLabels take precedence over rule labels and result labels
|
// 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 {
|
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.New(alertRule.GetKey().LogContext()...)
|
||||||
logger.Debug("state manager processing evaluation results", "resultCount", len(results))
|
logger.Debug("State manager processing evaluation results", "resultCount", len(results))
|
||||||
var states []*State
|
var states []*State
|
||||||
processedResults := make(map[string]*State, len(results))
|
processedResults := make(map[string]*State, len(results))
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
s := st.setNextState(ctx, alertRule, result, extraLabels)
|
s := st.setNextState(ctx, alertRule, result, extraLabels, logger)
|
||||||
states = append(states, s)
|
states = append(states, s)
|
||||||
processedResults[s.CacheID] = s
|
processedResults[s.CacheID] = s
|
||||||
}
|
}
|
||||||
resolvedStates := st.staleResultsHandler(ctx, evaluatedAt, alertRule, processedResults)
|
resolvedStates := st.staleResultsHandler(ctx, evaluatedAt, alertRule, processedResults, logger)
|
||||||
if len(states) > 0 {
|
if len(states) > 0 {
|
||||||
logger.Debug("saving new states to the database", "count", len(states))
|
logger.Debug("Saving new states to the database", "count", len(states))
|
||||||
_, _ = st.saveAlertStates(ctx, states...)
|
_, _ = st.saveAlertStates(ctx, states...)
|
||||||
}
|
}
|
||||||
return append(states, resolvedStates...)
|
return append(states, resolvedStates...)
|
||||||
@@ -211,7 +211,7 @@ func (st *Manager) maybeTakeScreenshot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the current state based on evaluation results
|
// Set the current state based on evaluation results
|
||||||
func (st *Manager) setNextState(ctx context.Context, alertRule *ngModels.AlertRule, result eval.Result, extraLabels data.Labels) *State {
|
func (st *Manager) setNextState(ctx context.Context, alertRule *ngModels.AlertRule, result eval.Result, extraLabels data.Labels, logger log.Logger) *State {
|
||||||
currentState := st.cache.getOrCreate(ctx, st.log, alertRule, result, extraLabels, st.externalURL)
|
currentState := st.cache.getOrCreate(ctx, st.log, alertRule, result, extraLabels, st.externalURL)
|
||||||
|
|
||||||
currentState.LastEvaluationTime = result.EvaluatedAt
|
currentState.LastEvaluationTime = result.EvaluatedAt
|
||||||
@@ -227,7 +227,7 @@ func (st *Manager) setNextState(ctx context.Context, alertRule *ngModels.AlertRu
|
|||||||
oldState := currentState.State
|
oldState := currentState.State
|
||||||
oldReason := currentState.StateReason
|
oldReason := currentState.StateReason
|
||||||
|
|
||||||
st.log.Debug("setting alert state", "uid", alertRule.UID)
|
logger.Debug("Setting alert state")
|
||||||
switch result.State {
|
switch result.State {
|
||||||
case eval.Normal:
|
case eval.Normal:
|
||||||
currentState.resultNormal(alertRule, result)
|
currentState.resultNormal(alertRule, result)
|
||||||
@@ -255,8 +255,7 @@ func (st *Manager) setNextState(ctx context.Context, alertRule *ngModels.AlertRu
|
|||||||
|
|
||||||
err := st.maybeTakeScreenshot(ctx, alertRule, currentState, oldState)
|
err := st.maybeTakeScreenshot(ctx, alertRule, currentState, oldState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st.log.Warn("failed to generate a screenshot for an alert instance",
|
logger.Warn("Failed to generate a screenshot for an alert instance",
|
||||||
"alert_rule", alertRule.UID,
|
|
||||||
"dashboard", alertRule.DashboardUID,
|
"dashboard", alertRule.DashboardUID,
|
||||||
"panel", alertRule.PanelID,
|
"panel", alertRule.PanelID,
|
||||||
"error", err)
|
"error", err)
|
||||||
@@ -287,10 +286,10 @@ func (st *Manager) recordMetrics() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
st.log.Debug("recording state cache metrics", "now", st.clock.Now())
|
st.log.Debug("Recording state cache metrics", "now", st.clock.Now())
|
||||||
st.cache.recordMetrics(st.metrics)
|
st.cache.recordMetrics(st.metrics)
|
||||||
case <-st.quit:
|
case <-st.quit:
|
||||||
st.log.Debug("stopping state cache metrics recording", "now", st.clock.Now())
|
st.log.Debug("Stopping state cache metrics recording", "now", st.clock.Now())
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -305,7 +304,7 @@ func (st *Manager) Put(states []*State) {
|
|||||||
|
|
||||||
// TODO: Is the `State` type necessary? Should it embed the instance?
|
// TODO: Is the `State` type necessary? Should it embed the instance?
|
||||||
func (st *Manager) saveAlertStates(ctx context.Context, states ...*State) (saved, failed int) {
|
func (st *Manager) saveAlertStates(ctx context.Context, states ...*State) (saved, failed int) {
|
||||||
st.log.Debug("saving alert states", "count", len(states))
|
st.log.Debug("Saving alert states", "count", len(states))
|
||||||
instances := make([]ngModels.AlertInstance, 0, len(states))
|
instances := make([]ngModels.AlertInstance, 0, len(states))
|
||||||
|
|
||||||
type debugInfo struct {
|
type debugInfo struct {
|
||||||
@@ -321,7 +320,7 @@ func (st *Manager) saveAlertStates(ctx context.Context, states ...*State) (saved
|
|||||||
_, hash, err := labels.StringAndHash()
|
_, hash, err := labels.StringAndHash()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug = append(debug, debugInfo{s.OrgID, s.AlertRuleUID, s.State.String(), s.Labels.String()})
|
debug = append(debug, debugInfo{s.OrgID, s.AlertRuleUID, s.State.String(), s.Labels.String()})
|
||||||
st.log.Error("failed to save alert instance with invalid labels", "orgID", s.OrgID, "ruleUID", s.AlertRuleUID, "error", err)
|
st.log.Error("Failed to save alert instance with invalid labels", "orgID", s.OrgID, "rule", s.AlertRuleUID, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fields := ngModels.AlertInstance{
|
fields := ngModels.AlertInstance{
|
||||||
@@ -344,7 +343,7 @@ func (st *Manager) saveAlertStates(ctx context.Context, states ...*State) (saved
|
|||||||
for _, inst := range instances {
|
for _, inst := range instances {
|
||||||
debug = append(debug, debugInfo{inst.RuleOrgID, inst.RuleUID, string(inst.CurrentState), data.Labels(inst.Labels).String()})
|
debug = append(debug, debugInfo{inst.RuleOrgID, inst.RuleUID, string(inst.CurrentState), data.Labels(inst.Labels).String()})
|
||||||
}
|
}
|
||||||
st.log.Error("failed to save alert states", "states", debug, "error", err)
|
st.log.Error("Failed to save alert states", "states", debug, "error", err)
|
||||||
return 0, len(debug)
|
return 0, len(debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +376,7 @@ func (i InstanceStateAndReason) String() string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Time, alertRule *ngModels.AlertRule, states map[string]*State) []*State {
|
func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Time, alertRule *ngModels.AlertRule, states map[string]*State, logger log.Logger) []*State {
|
||||||
var resolvedStates []*State
|
var resolvedStates []*State
|
||||||
allStates := st.GetStatesForRuleUID(alertRule.OrgID, alertRule.UID)
|
allStates := st.GetStatesForRuleUID(alertRule.OrgID, alertRule.UID)
|
||||||
toDelete := make([]ngModels.AlertInstanceKey, 0)
|
toDelete := make([]ngModels.AlertInstanceKey, 0)
|
||||||
@@ -385,12 +384,12 @@ func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Tim
|
|||||||
for _, s := range allStates {
|
for _, s := range allStates {
|
||||||
// Is the cached state in our recently processed results? If not, is it stale?
|
// Is the cached state in our recently processed results? If not, is it stale?
|
||||||
if _, ok := states[s.CacheID]; !ok && stateIsStale(evaluatedAt, s.LastEvaluationTime, alertRule.IntervalSeconds) {
|
if _, ok := states[s.CacheID]; !ok && stateIsStale(evaluatedAt, s.LastEvaluationTime, alertRule.IntervalSeconds) {
|
||||||
st.log.Debug("removing stale state entry", "orgID", s.OrgID, "alertRuleUID", s.AlertRuleUID, "cacheID", s.CacheID)
|
logger.Info("Removing stale state entry", "cacheID", s.CacheID, "state", s.State, "reason", s.StateReason)
|
||||||
st.cache.deleteEntry(s.OrgID, s.AlertRuleUID, s.CacheID)
|
st.cache.deleteEntry(s.OrgID, s.AlertRuleUID, s.CacheID)
|
||||||
ilbs := ngModels.InstanceLabels(s.Labels)
|
ilbs := ngModels.InstanceLabels(s.Labels)
|
||||||
_, labelsHash, err := ilbs.StringAndHash()
|
_, labelsHash, err := ilbs.StringAndHash()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st.log.Error("unable to get labelsHash", "error", err.Error(), "orgID", s.OrgID, "alertRuleUID", s.AlertRuleUID)
|
logger.Error("Unable to get labelsHash", "error", err.Error(), s.AlertRuleUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
toDelete = append(toDelete, ngModels.AlertInstanceKey{RuleOrgID: s.OrgID, RuleUID: s.AlertRuleUID, LabelsHash: labelsHash})
|
toDelete = append(toDelete, ngModels.AlertInstanceKey{RuleOrgID: s.OrgID, RuleUID: s.AlertRuleUID, LabelsHash: labelsHash})
|
||||||
@@ -411,8 +410,7 @@ func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Tim
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := st.instanceStore.DeleteAlertInstances(ctx, toDelete...); err != nil {
|
if err := st.instanceStore.DeleteAlertInstances(ctx, toDelete...); err != nil {
|
||||||
st.log.Error("unable to delete stale instances from database", "error", err.Error(),
|
logger.Error("Unable to delete stale instances from database", "error", err, "count", len(toDelete))
|
||||||
"orgID", alertRule.OrgID, "alertRuleUID", alertRule.UID, "count", len(toDelete))
|
|
||||||
}
|
}
|
||||||
return resolvedStates
|
return resolvedStates
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
@@ -91,7 +90,7 @@ func Test_maybeNewImage(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.description, func(t *testing.T) {
|
t.Run(test.description, func(t *testing.T) {
|
||||||
imageService := &CountingImageService{}
|
imageService := &CountingImageService{}
|
||||||
mgr := NewManager(log.NewNopLogger(), &metrics.State{}, nil,
|
mgr := NewManager(&metrics.State{}, nil,
|
||||||
&FakeRuleReader{}, &FakeInstanceStore{},
|
&FakeRuleReader{}, &FakeInstanceStore{},
|
||||||
imageService, clock.NewMock(), &FakeHistorian{})
|
imageService, clock.NewMock(), &FakeHistorian{})
|
||||||
err := mgr.maybeTakeScreenshot(context.Background(), &ngmodels.AlertRule{}, test.state, test.oldState)
|
err := mgr.maybeTakeScreenshot(context.Background(), &ngmodels.AlertRule{}, test.state, test.oldState)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@@ -39,8 +38,8 @@ func TestDashboardAnnotations(t *testing.T) {
|
|||||||
_, dbstore := tests.SetupTestEnv(t, 1)
|
_, dbstore := tests.SetupTestEnv(t, 1)
|
||||||
|
|
||||||
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
hist := historian.NewAnnotationHistorian(fakeAnnoRepo, &dashboards.FakeDashboardService{}, log.NewNopLogger())
|
hist := historian.NewAnnotationHistorian(fakeAnnoRepo, &dashboards.FakeDashboardService{})
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.New(), hist)
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.New(), hist)
|
||||||
|
|
||||||
const mainOrgID int64 = 1
|
const mainOrgID int64 = 1
|
||||||
|
|
||||||
@@ -2012,8 +2011,8 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
hist := historian.NewAnnotationHistorian(fakeAnnoRepo, &dashboards.FakeDashboardService{}, log.NewNopLogger())
|
hist := historian.NewAnnotationHistorian(fakeAnnoRepo, &dashboards.FakeDashboardService{})
|
||||||
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, &state.FakeInstanceStore{}, &image.NotAvailableImageService{}, clock.New(), hist)
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, nil, &state.FakeInstanceStore{}, &image.NotAvailableImageService{}, clock.New(), hist)
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
for _, res := range tc.evalResults {
|
for _, res := range tc.evalResults {
|
||||||
_ = st.ProcessEvalResults(context.Background(), evaluationTime, tc.alertRule, res, data.Labels{
|
_ = st.ProcessEvalResults(context.Background(), evaluationTime, tc.alertRule, res, data.Labels{
|
||||||
@@ -2040,7 +2039,7 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
t.Run("should save state to database", func(t *testing.T) {
|
t.Run("should save state to database", func(t *testing.T) {
|
||||||
instanceStore := &state.FakeInstanceStore{}
|
instanceStore := &state.FakeInstanceStore{}
|
||||||
clk := clock.New()
|
clk := clock.New()
|
||||||
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, instanceStore, &image.NotAvailableImageService{}, clk, &state.FakeHistorian{})
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, nil, instanceStore, &image.NotAvailableImageService{}, clk, &state.FakeHistorian{})
|
||||||
rule := models.AlertRuleGen()()
|
rule := models.AlertRuleGen()()
|
||||||
var results = eval.GenerateResults(rand.Intn(4)+1, eval.ResultGen(eval.WithEvaluatedAt(clk.Now())))
|
var results = eval.GenerateResults(rand.Intn(4)+1, eval.ResultGen(eval.WithEvaluatedAt(clk.Now())))
|
||||||
|
|
||||||
@@ -2169,7 +2168,7 @@ func TestStaleResultsHandler(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.New(), &state.FakeHistorian{})
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.New(), &state.FakeHistorian{})
|
||||||
st.Warm(ctx)
|
st.Warm(ctx)
|
||||||
existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID)
|
existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID)
|
||||||
|
|
||||||
@@ -2231,7 +2230,7 @@ func TestStaleResults(t *testing.T) {
|
|||||||
clk := clock.NewMock()
|
clk := clock.NewMock()
|
||||||
clk.Set(time.Now())
|
clk.Set(time.Now())
|
||||||
|
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clk, &state.FakeHistorian{})
|
st := state.NewManager(testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clk, &state.FakeHistorian{})
|
||||||
|
|
||||||
orgID := rand.Int63()
|
orgID := rand.Int63()
|
||||||
rule := tests.CreateTestAlertRule(t, ctx, dbstore, 10, orgID)
|
rule := tests.CreateTestAlertRule(t, ctx, dbstore, 10, orgID)
|
||||||
|
|||||||
Reference in New Issue
Block a user