Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code enhancements for future use v2 #199

Merged
merged 2 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions internal/config/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@ func (r *RuntimeConfig) fetchRules(ctx context.Context, tx *sqlx.Tx) error {

rulesByID := make(map[int64]*rule.Rule)
for _, ru := range rules {
ruleLogger := r.logger.With(
zap.Int64("id", ru.ID),
zap.String("name", ru.Name),
zap.String("object_filter", ru.ObjectFilterExpr.String),
zap.Int64("timeperiod_id", ru.TimePeriodID.Int64),
)
ruleLogger := r.logger.With(zap.Inline(ru))

if ru.ObjectFilterExpr.Valid {
f, err := filter.Parse(ru.ObjectFilterExpr.String)
Expand Down Expand Up @@ -134,12 +129,7 @@ func (r *RuntimeConfig) applyPendingRules() {
if pendingRule == nil {
delete(r.Rules, id)
} else {
ruleLogger := r.logger.With(
zap.Int64("id", pendingRule.ID),
zap.String("name", pendingRule.Name),
zap.String("object_filter", pendingRule.ObjectFilterExpr.String),
zap.Int64("timeperiod_id", pendingRule.TimePeriodID.Int64),
)
ruleLogger := r.logger.With(zap.Inline(pendingRule))

if pendingRule.TimePeriodID.Valid {
if p := r.TimePeriods[pendingRule.TimePeriodID.Int64]; p == nil {
Expand Down
57 changes: 22 additions & 35 deletions internal/incident/incident.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,23 +360,21 @@ func (i *Incident) evaluateRules(ctx context.Context, tx *sqlx.Tx, eventID int64
}

if _, ok := i.Rules[r.ID]; !ok {
if r.ObjectFilter != nil {
matched, err := r.ObjectFilter.Eval(i.Object)
if err != nil {
i.logger.Warnw("Failed to evaluate object filter", zap.String("rule", r.Name), zap.Error(err))
}
matched, err := r.Eval(i.Object)
if err != nil {
i.logger.Warnw("Failed to evaluate object filter", zap.Object("rule", r), zap.Error(err))
}

if err != nil || !matched {
continue
}
if err != nil || !matched {
continue
}

i.Rules[r.ID] = struct{}{}
i.logger.Infof("Rule %q matches", r.Name)
i.logger.Infow("Rule matches", zap.Object("rule", r))

err := i.AddRuleMatched(ctx, tx, r)
err = i.AddRuleMatched(ctx, tx, r)
if err != nil {
i.logger.Errorw("Failed to upsert incident rule", zap.String("rule", r.Name), zap.Error(err))
i.logger.Errorw("Failed to upsert incident rule", zap.Object("rule", r), zap.Error(err))

return errors.New("failed to insert incident rule")
}
Expand All @@ -389,7 +387,7 @@ func (i *Incident) evaluateRules(ctx context.Context, tx *sqlx.Tx, eventID int64
Type: RuleMatched,
}
if err := hr.Sync(ctx, i.db, tx); err != nil {
i.logger.Errorw("Failed to insert rule matched incident history", zap.String("rule", r.Name), zap.Error(err))
i.logger.Errorw("Failed to insert rule matched incident history", zap.Object("rule", r), zap.Error(err))

return errors.New("failed to insert rule matched incident history")
}
Expand Down Expand Up @@ -429,27 +427,16 @@ func (i *Incident) evaluateEscalations(eventTime time.Time) ([]*rule.Escalation,
// Check if new escalation stages are reached
for _, escalation := range r.Escalations {
if _, ok := i.EscalationState[escalation.ID]; !ok {
matched := false

if escalation.Condition == nil {
matched = true
matched, err := escalation.Eval(filterContext)
if err != nil {
i.logger.Warnw(
"Failed to evaluate escalation condition", zap.Object("rule", r),
zap.Object("escalation", escalation), zap.Error(err),
)
} else if !matched {
incidentAgeFilter := filterContext.ReevaluateAfter(escalation.Condition)
retryAfter = min(retryAfter, incidentAgeFilter)
} else {
var err error
matched, err = escalation.Condition.Eval(filterContext)
if err != nil {
i.logger.Warnw(
"Failed to evaluate escalation condition", zap.String("rule", r.Name),
zap.Object("escalation", escalation), zap.Error(err),
)

matched = false
} else if !matched {
incidentAgeFilter := filterContext.ReevaluateAfter(escalation.Condition)
retryAfter = min(retryAfter, incidentAgeFilter)
}
}

if matched {
escalations = append(escalations, escalation)
}
}
Expand Down Expand Up @@ -483,14 +470,14 @@ func (i *Incident) evaluateEscalations(eventTime time.Time) ([]*rule.Escalation,
func (i *Incident) triggerEscalations(ctx context.Context, tx *sqlx.Tx, ev *event.Event, escalations []*rule.Escalation) error {
for _, escalation := range escalations {
r := i.runtimeConfig.Rules[escalation.RuleID]
i.logger.Infow("Rule reached escalation", zap.String("rule", r.Name), zap.Object("escalation", escalation))
i.logger.Infow("Rule reached escalation", zap.Object("rule", r), zap.Object("escalation", escalation))

state := &EscalationState{RuleEscalationID: escalation.ID, TriggeredAt: types.UnixMilli(time.Now())}
i.EscalationState[escalation.ID] = state

if err := i.AddEscalationTriggered(ctx, tx, state); err != nil {
i.logger.Errorw(
"Failed to upsert escalation state", zap.String("rule", r.Name),
"Failed to upsert escalation state", zap.Object("rule", r),
zap.Object("escalation", escalation), zap.Error(err),
)

Expand All @@ -508,7 +495,7 @@ func (i *Incident) triggerEscalations(ctx context.Context, tx *sqlx.Tx, ev *even

if err := hr.Sync(ctx, i.db, tx); err != nil {
i.logger.Errorw(
"Failed to insert escalation triggered incident history", zap.String("rule", r.Name),
"Failed to insert escalation triggered incident history", zap.Object("rule", r),
zap.Object("escalation", escalation), zap.Error(err),
)

Expand Down
10 changes: 10 additions & 0 deletions internal/rule/escalation.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ func (e *Escalation) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
return nil
}

// Eval evaluates the configured escalation filter for the provided filter.
// Returns always true if there are no configured escalation conditions.
func (e *Escalation) Eval(filterable *EscalationFilter) (bool, error) {
if e.Condition == nil {
return true, nil
}

return e.Condition.Eval(filterable)
}

func (e *Escalation) DisplayName() string {
if e.NameRaw.Valid && e.NameRaw.String != "" {
return e.NameRaw.String
Expand Down
26 changes: 26 additions & 0 deletions internal/rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/icinga/icinga-notifications/internal/filter"
"github.com/icinga/icinga-notifications/internal/recipient"
"github.com/icinga/icinga-notifications/internal/timeperiod"
"go.uber.org/zap/zapcore"
"time"
)

Expand All @@ -19,6 +20,31 @@ type Rule struct {
Escalations map[int64]*Escalation `db:"-"`
}

// MarshalLogObject implements the zapcore.ObjectMarshaler interface.
func (r *Rule) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddInt64("id", r.ID)
encoder.AddString("name", r.Name)

if r.TimePeriodID.Valid && r.TimePeriodID.Int64 != 0 {
encoder.AddInt64("timeperiod_id", r.TimePeriodID.Int64)
}
if r.ObjectFilterExpr.Valid && r.ObjectFilterExpr.String != "" {
encoder.AddString("object_filter", r.ObjectFilterExpr.String)
}

return nil
}

// Eval evaluates the configured object filter for the provided filterable.
// Returns always true if the current rule doesn't have a configured object filter.
func (r *Rule) Eval(filterable filter.Filterable) (bool, error) {
if r.ObjectFilter == nil {
return true, nil
}

return r.ObjectFilter.Eval(filterable)
}

// ContactChannels stores a set of channel IDs for each set of individual contacts.
type ContactChannels map[*recipient.Contact]map[int64]bool

Expand Down
Loading