mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 19:08:59 +08:00
feat: added user selected filtering of channels in alerts (#1459)
This commit is contained in:
parent
425b732370
commit
80c96af5a4
@ -40,6 +40,8 @@ type Alert struct {
|
|||||||
StartsAt time.Time `json:"startsAt,omitempty"`
|
StartsAt time.Time `json:"startsAt,omitempty"`
|
||||||
EndsAt time.Time `json:"endsAt,omitempty"`
|
EndsAt time.Time `json:"endsAt,omitempty"`
|
||||||
GeneratorURL string `json:"generatorURL,omitempty"`
|
GeneratorURL string `json:"generatorURL,omitempty"`
|
||||||
|
|
||||||
|
Receivers []string `json:"receivers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns the name of the alert. It is equivalent to the "alertname" label.
|
// Name returns the name of the alert. It is equivalent to the "alertname" label.
|
||||||
@ -53,7 +55,7 @@ func (a *Alert) Hash() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Alert) String() string {
|
func (a *Alert) String() string {
|
||||||
s := fmt.Sprintf("%s[%s]", a.Name(), fmt.Sprintf("%016x", a.Hash())[:7])
|
s := fmt.Sprintf("%s[%s][%s]", a.Name(), fmt.Sprintf("%016x", a.Hash())[:7], a.Receivers)
|
||||||
if a.Resolved() {
|
if a.Resolved() {
|
||||||
return s + "[resolved]"
|
return s + "[resolved]"
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,9 @@ type Alert struct {
|
|||||||
|
|
||||||
GeneratorURL string
|
GeneratorURL string
|
||||||
|
|
||||||
|
// list of preferred receivers, e.g. slack
|
||||||
|
Receivers []string
|
||||||
|
|
||||||
Value float64
|
Value float64
|
||||||
ActiveAt time.Time
|
ActiveAt time.Time
|
||||||
FiredAt time.Time
|
FiredAt time.Time
|
||||||
@ -80,7 +83,6 @@ type Alert struct {
|
|||||||
ValidUntil time.Time
|
ValidUntil time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(amol): need to review this with ankit
|
|
||||||
func (a *Alert) needsSending(ts time.Time, resendDelay time.Duration) bool {
|
func (a *Alert) needsSending(ts time.Time, resendDelay time.Duration) bool {
|
||||||
if a.State == StatePending {
|
if a.State == StatePending {
|
||||||
return false
|
return false
|
||||||
|
@ -35,6 +35,8 @@ type PostableRule struct {
|
|||||||
// Source captures the source url where rule has been created
|
// Source captures the source url where rule has been created
|
||||||
Source string `json:"source,omitempty"`
|
Source string `json:"source,omitempty"`
|
||||||
|
|
||||||
|
PreferredChannels []string `json:"preferredChannels,omitempty"`
|
||||||
|
|
||||||
// legacy
|
// legacy
|
||||||
Expr string `yaml:"expr,omitempty" json:"expr,omitempty"`
|
Expr string `yaml:"expr,omitempty" json:"expr,omitempty"`
|
||||||
OldYaml string `json:"yaml,omitempty"`
|
OldYaml string `json:"yaml,omitempty"`
|
||||||
|
@ -381,12 +381,7 @@ func (m *Manager) prepareTask(acquireLock bool, r *PostableRule, taskName string
|
|||||||
// create a threshold rule
|
// create a threshold rule
|
||||||
tr, err := NewThresholdRule(
|
tr, err := NewThresholdRule(
|
||||||
ruleId,
|
ruleId,
|
||||||
r.Alert,
|
r,
|
||||||
r.RuleCondition,
|
|
||||||
time.Duration(r.EvalWindow),
|
|
||||||
r.Labels,
|
|
||||||
r.Annotations,
|
|
||||||
r.Source,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -406,14 +401,8 @@ func (m *Manager) prepareTask(acquireLock bool, r *PostableRule, taskName string
|
|||||||
// create promql rule
|
// create promql rule
|
||||||
pr, err := NewPromRule(
|
pr, err := NewPromRule(
|
||||||
ruleId,
|
ruleId,
|
||||||
r.Alert,
|
r,
|
||||||
r.RuleCondition,
|
|
||||||
time.Duration(r.EvalWindow),
|
|
||||||
r.Labels,
|
|
||||||
r.Annotations,
|
|
||||||
// required as promql engine works with logger and not zap
|
|
||||||
log.With(m.logger, "alert", r.Alert),
|
log.With(m.logger, "alert", r.Alert),
|
||||||
r.Source,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -521,6 +510,7 @@ func (m *Manager) prepareNotifyFunc() NotifyFunc {
|
|||||||
Labels: alert.Labels,
|
Labels: alert.Labels,
|
||||||
Annotations: alert.Annotations,
|
Annotations: alert.Annotations,
|
||||||
GeneratorURL: generatorURL,
|
GeneratorURL: generatorURL,
|
||||||
|
Receivers: alert.Receivers,
|
||||||
}
|
}
|
||||||
if !alert.ResolvedAt.IsZero() {
|
if !alert.ResolvedAt.IsZero() {
|
||||||
a.EndsAt = alert.ResolvedAt
|
a.EndsAt = alert.ResolvedAt
|
||||||
|
@ -29,6 +29,8 @@ type PromRule struct {
|
|||||||
labels plabels.Labels
|
labels plabels.Labels
|
||||||
annotations plabels.Labels
|
annotations plabels.Labels
|
||||||
|
|
||||||
|
preferredChannels []string
|
||||||
|
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
evaluationDuration time.Duration
|
evaluationDuration time.Duration
|
||||||
evaluationTimestamp time.Time
|
evaluationTimestamp time.Time
|
||||||
@ -45,38 +47,37 @@ type PromRule struct {
|
|||||||
|
|
||||||
func NewPromRule(
|
func NewPromRule(
|
||||||
id string,
|
id string,
|
||||||
name string,
|
postableRule *PostableRule,
|
||||||
ruleCondition *RuleCondition,
|
|
||||||
evalWindow time.Duration,
|
|
||||||
labels, annotations map[string]string,
|
|
||||||
logger log.Logger,
|
logger log.Logger,
|
||||||
source string,
|
|
||||||
) (*PromRule, error) {
|
) (*PromRule, error) {
|
||||||
|
|
||||||
if int64(evalWindow) == 0 {
|
if postableRule.RuleCondition == nil {
|
||||||
evalWindow = 5 * time.Minute
|
|
||||||
}
|
|
||||||
|
|
||||||
if ruleCondition == nil {
|
|
||||||
return nil, fmt.Errorf("no rule condition")
|
return nil, fmt.Errorf("no rule condition")
|
||||||
} else if !ruleCondition.IsValid() {
|
} else if !postableRule.RuleCondition.IsValid() {
|
||||||
return nil, fmt.Errorf("invalid rule condition")
|
return nil, fmt.Errorf("invalid rule condition")
|
||||||
}
|
}
|
||||||
|
|
||||||
zap.S().Info("msg:", "creating new alerting rule", "\t name:", name, "\t condition:", ruleCondition.String())
|
p := PromRule{
|
||||||
|
id: id,
|
||||||
|
name: postableRule.Alert,
|
||||||
|
source: postableRule.Source,
|
||||||
|
ruleCondition: postableRule.RuleCondition,
|
||||||
|
evalWindow: time.Duration(postableRule.EvalWindow),
|
||||||
|
labels: plabels.FromMap(postableRule.Labels),
|
||||||
|
annotations: plabels.FromMap(postableRule.Annotations),
|
||||||
|
preferredChannels: postableRule.PreferredChannels,
|
||||||
|
health: HealthUnknown,
|
||||||
|
active: map[uint64]*Alert{},
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
return &PromRule{
|
if int64(p.evalWindow) == 0 {
|
||||||
id: id,
|
p.evalWindow = 5 * time.Minute
|
||||||
name: name,
|
}
|
||||||
source: source,
|
|
||||||
ruleCondition: ruleCondition,
|
zap.S().Info("msg:", "creating new alerting rule", "\t name:", p.name, "\t condition:", p.ruleCondition.String())
|
||||||
evalWindow: evalWindow,
|
|
||||||
labels: plabels.FromMap(labels),
|
return &p, nil
|
||||||
annotations: plabels.FromMap(annotations),
|
|
||||||
health: HealthUnknown,
|
|
||||||
active: map[uint64]*Alert{},
|
|
||||||
logger: logger,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromRule) Name() string {
|
func (r *PromRule) Name() string {
|
||||||
@ -99,6 +100,10 @@ func (r *PromRule) GeneratorURL() string {
|
|||||||
return prepareRuleGeneratorURL(r.ID(), r.source)
|
return prepareRuleGeneratorURL(r.ID(), r.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *PromRule) PreferredChannels() []string {
|
||||||
|
return r.preferredChannels
|
||||||
|
}
|
||||||
|
|
||||||
func (r *PromRule) SetLastError(err error) {
|
func (r *PromRule) SetLastError(err error) {
|
||||||
r.mtx.Lock()
|
r.mtx.Lock()
|
||||||
defer r.mtx.Unlock()
|
defer r.mtx.Unlock()
|
||||||
@ -382,6 +387,7 @@ func (r *PromRule) Eval(ctx context.Context, ts time.Time, queriers *Queriers) (
|
|||||||
State: StatePending,
|
State: StatePending,
|
||||||
Value: smpl.V,
|
Value: smpl.V,
|
||||||
GeneratorURL: r.GeneratorURL(),
|
GeneratorURL: r.GeneratorURL(),
|
||||||
|
Receivers: r.preferredChannels,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +398,7 @@ func (r *PromRule) Eval(ctx context.Context, ts time.Time, queriers *Queriers) (
|
|||||||
if alert, ok := r.active[h]; ok && alert.State != StateInactive {
|
if alert, ok := r.active[h]; ok && alert.State != StateInactive {
|
||||||
alert.Value = a.Value
|
alert.Value = a.Value
|
||||||
alert.Annotations = a.Annotations
|
alert.Annotations = a.Annotations
|
||||||
|
alert.Receivers = r.preferredChannels
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,11 +436,12 @@ func (r *PromRule) Eval(ctx context.Context, ts time.Time, queriers *Queriers) (
|
|||||||
func (r *PromRule) String() string {
|
func (r *PromRule) String() string {
|
||||||
|
|
||||||
ar := PostableRule{
|
ar := PostableRule{
|
||||||
Alert: r.name,
|
Alert: r.name,
|
||||||
RuleCondition: r.ruleCondition,
|
RuleCondition: r.ruleCondition,
|
||||||
EvalWindow: Duration(r.evalWindow),
|
EvalWindow: Duration(r.evalWindow),
|
||||||
Labels: r.labels.Map(),
|
Labels: r.labels.Map(),
|
||||||
Annotations: r.annotations.Map(),
|
Annotations: r.annotations.Map(),
|
||||||
|
PreferredChannels: r.preferredChannels,
|
||||||
}
|
}
|
||||||
|
|
||||||
byt, err := yaml.Marshal(ar)
|
byt, err := yaml.Marshal(ar)
|
||||||
|
@ -19,6 +19,8 @@ type Rule interface {
|
|||||||
State() AlertState
|
State() AlertState
|
||||||
ActiveAlerts() []*Alert
|
ActiveAlerts() []*Alert
|
||||||
|
|
||||||
|
PreferredChannels() []string
|
||||||
|
|
||||||
Eval(context.Context, time.Time, *Queriers) (interface{}, error)
|
Eval(context.Context, time.Time, *Queriers) (interface{}, error)
|
||||||
String() string
|
String() string
|
||||||
// Query() string
|
// Query() string
|
||||||
|
@ -32,6 +32,7 @@ type ThresholdRule struct {
|
|||||||
labels labels.Labels
|
labels labels.Labels
|
||||||
annotations labels.Labels
|
annotations labels.Labels
|
||||||
|
|
||||||
|
preferredChannels []string
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
evaluationDuration time.Duration
|
evaluationDuration time.Duration
|
||||||
evaluationTimestamp time.Time
|
evaluationTimestamp time.Time
|
||||||
@ -46,39 +47,35 @@ type ThresholdRule struct {
|
|||||||
|
|
||||||
func NewThresholdRule(
|
func NewThresholdRule(
|
||||||
id string,
|
id string,
|
||||||
name string,
|
p *PostableRule,
|
||||||
ruleCondition *RuleCondition,
|
|
||||||
evalWindow time.Duration,
|
|
||||||
l, a map[string]string,
|
|
||||||
source string,
|
|
||||||
) (*ThresholdRule, error) {
|
) (*ThresholdRule, error) {
|
||||||
|
|
||||||
if int64(evalWindow) == 0 {
|
if p.RuleCondition == nil {
|
||||||
evalWindow = 5 * time.Minute
|
|
||||||
}
|
|
||||||
|
|
||||||
if ruleCondition == nil {
|
|
||||||
return nil, fmt.Errorf("no rule condition")
|
return nil, fmt.Errorf("no rule condition")
|
||||||
} else if !ruleCondition.IsValid() {
|
} else if !p.RuleCondition.IsValid() {
|
||||||
return nil, fmt.Errorf("invalid rule condition")
|
return nil, fmt.Errorf("invalid rule condition")
|
||||||
}
|
}
|
||||||
|
|
||||||
thresholdRule := &ThresholdRule{
|
t := ThresholdRule{
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: p.Alert,
|
||||||
source: source,
|
source: p.Source,
|
||||||
ruleCondition: ruleCondition,
|
ruleCondition: p.RuleCondition,
|
||||||
evalWindow: evalWindow,
|
evalWindow: time.Duration(p.EvalWindow),
|
||||||
labels: labels.FromMap(l),
|
labels: labels.FromMap(p.Labels),
|
||||||
annotations: labels.FromMap(a),
|
annotations: labels.FromMap(p.Annotations),
|
||||||
|
preferredChannels: p.PreferredChannels,
|
||||||
health: HealthUnknown,
|
health: HealthUnknown,
|
||||||
active: map[uint64]*Alert{},
|
active: map[uint64]*Alert{},
|
||||||
}
|
}
|
||||||
|
|
||||||
zap.S().Info("msg:", "creating new alerting rule", "\t name:", name, "\t condition:", ruleCondition.String(), "\t generatorURL:", thresholdRule.GeneratorURL())
|
if int64(t.evalWindow) == 0 {
|
||||||
|
t.evalWindow = 5 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
return thresholdRule, nil
|
zap.S().Info("msg:", "creating new alerting rule", "\t name:", t.name, "\t condition:", t.ruleCondition.String(), "\t generatorURL:", t.GeneratorURL())
|
||||||
|
|
||||||
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ThresholdRule) Name() string {
|
func (r *ThresholdRule) Name() string {
|
||||||
@ -97,6 +94,10 @@ func (r *ThresholdRule) GeneratorURL() string {
|
|||||||
return prepareRuleGeneratorURL(r.ID(), r.source)
|
return prepareRuleGeneratorURL(r.ID(), r.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ThresholdRule) PreferredChannels() []string {
|
||||||
|
return r.preferredChannels
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ThresholdRule) target() *float64 {
|
func (r *ThresholdRule) target() *float64 {
|
||||||
if r.ruleCondition == nil {
|
if r.ruleCondition == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -479,6 +480,7 @@ func (r *ThresholdRule) runChQuery(ctx context.Context, db clickhouse.Conn, quer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
zap.S().Debugf("ruleid:", r.ID(), "\t resultmap(potential alerts):", len(resultMap))
|
||||||
|
|
||||||
for _, sample := range resultMap {
|
for _, sample := range resultMap {
|
||||||
// check alert rule condition before dumping results
|
// check alert rule condition before dumping results
|
||||||
@ -486,7 +488,7 @@ func (r *ThresholdRule) runChQuery(ctx context.Context, db clickhouse.Conn, quer
|
|||||||
result = append(result, sample)
|
result = append(result, sample)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
zap.S().Debugf("ruleid:", r.ID(), "\t result (found alerts):", len(result))
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,6 +617,7 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time, queriers *Querie
|
|||||||
State: StatePending,
|
State: StatePending,
|
||||||
Value: smpl.V,
|
Value: smpl.V,
|
||||||
GeneratorURL: r.GeneratorURL(),
|
GeneratorURL: r.GeneratorURL(),
|
||||||
|
Receivers: r.preferredChannels,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,6 +631,7 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time, queriers *Querie
|
|||||||
|
|
||||||
alert.Value = a.Value
|
alert.Value = a.Value
|
||||||
alert.Annotations = a.Annotations
|
alert.Annotations = a.Annotations
|
||||||
|
alert.Receivers = r.preferredChannels
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,11 +669,12 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time, queriers *Querie
|
|||||||
func (r *ThresholdRule) String() string {
|
func (r *ThresholdRule) String() string {
|
||||||
|
|
||||||
ar := PostableRule{
|
ar := PostableRule{
|
||||||
Alert: r.name,
|
Alert: r.name,
|
||||||
RuleCondition: r.ruleCondition,
|
RuleCondition: r.ruleCondition,
|
||||||
EvalWindow: Duration(r.evalWindow),
|
EvalWindow: Duration(r.evalWindow),
|
||||||
Labels: r.labels.Map(),
|
Labels: r.labels.Map(),
|
||||||
Annotations: r.annotations.Map(),
|
Annotations: r.annotations.Map(),
|
||||||
|
PreferredChannels: r.preferredChannels,
|
||||||
}
|
}
|
||||||
|
|
||||||
byt, err := yaml.Marshal(ar)
|
byt, err := yaml.Marshal(ar)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user