influxdb/chronograf/.kapacitor/triggers.go

163 lines
3.9 KiB
Go

package kapacitor
import (
"fmt"
"github.com/influxdata/platform/chronograf"
)
const (
// Deadman triggers when data is missing for a period of time
Deadman = "deadman"
// Relative triggers when the value has changed compared to the past
Relative = "relative"
// Threshold triggers when value crosses a threshold
Threshold = "threshold"
// ThresholdRange triggers when a value is inside or outside a range
ThresholdRange = "range"
// ChangePercent triggers a relative alert when value changed by a percentage
ChangePercent = "% change"
// ChangeAmount triggers a relative alert when the value change by some amount
ChangeAmount = "change"
)
// AllAlerts are properties all alert types will have
var AllAlerts = `
.message(message)
.id(idVar)
.idTag(idTag)
.levelTag(levelTag)
.messageField(messageField)
.durationField(durationField)
`
// Details is used only for alerts that specify detail string
var Details = `
.details(details)
`
// ThresholdTrigger is the tickscript trigger for alerts that exceed a value
var ThresholdTrigger = `
var trigger = data
|alert()
.crit(lambda: "value" %s crit)
`
// ThresholdRangeTrigger is the alert when data does not intersect the range.
var ThresholdRangeTrigger = `
var trigger = data
|alert()
.crit(lambda: "value" %s lower %s "value" %s upper)
`
// RelativeAbsoluteTrigger compares one window of data versus another (current - past)
var RelativeAbsoluteTrigger = `
var past = data
|shift(shift)
var current = data
var trigger = past
|join(current)
.as('past', 'current')
|eval(lambda: float("current.value" - "past.value"))
.keep()
.as('value')
|alert()
.crit(lambda: "value" %s crit)
`
// RelativePercentTrigger compares one window of data versus another as a percent change.
var RelativePercentTrigger = `
var past = data
|shift(shift)
var current = data
var trigger = past
|join(current)
.as('past', 'current')
|eval(lambda: abs(float("current.value" - "past.value"))/float("past.value") * 100.0)
.keep()
.as('value')
|alert()
.crit(lambda: "value" %s crit)
`
// DeadmanTrigger checks if any data has been streamed in the last period of time
var DeadmanTrigger = `
var trigger = data|deadman(threshold, period)
`
// Trigger returns the trigger mechanism for a tickscript
func Trigger(rule chronograf.AlertRule) (string, error) {
var trigger string
var err error
switch rule.Trigger {
case Deadman:
trigger, err = DeadmanTrigger, nil
case Relative:
trigger, err = relativeTrigger(rule)
case Threshold:
if rule.TriggerValues.RangeValue == "" {
trigger, err = thresholdTrigger(rule)
} else {
trigger, err = thresholdRangeTrigger(rule)
}
default:
trigger, err = "", fmt.Errorf("Unknown trigger type: %s", rule.Trigger)
}
if err != nil {
return "", err
}
// Only add stateChangesOnly to new rules
if rule.ID == "" {
trigger += `
.stateChangesOnly()
`
}
trigger += AllAlerts
if rule.Details != "" {
trigger += Details
}
return trigger, nil
}
func relativeTrigger(rule chronograf.AlertRule) (string, error) {
op, err := kapaOperator(rule.TriggerValues.Operator)
if err != nil {
return "", err
}
if rule.TriggerValues.Change == ChangePercent {
return fmt.Sprintf(RelativePercentTrigger, op), nil
} else if rule.TriggerValues.Change == ChangeAmount {
return fmt.Sprintf(RelativeAbsoluteTrigger, op), nil
} else {
return "", fmt.Errorf("Unknown change type %s", rule.TriggerValues.Change)
}
}
func thresholdTrigger(rule chronograf.AlertRule) (string, error) {
op, err := kapaOperator(rule.TriggerValues.Operator)
if err != nil {
return "", err
}
return fmt.Sprintf(ThresholdTrigger, op), nil
}
func thresholdRangeTrigger(rule chronograf.AlertRule) (string, error) {
ops, err := rangeOperators(rule.TriggerValues.Operator)
if err != nil {
return "", err
}
var iops = make([]interface{}, len(ops))
for i, o := range ops {
iops[i] = o
}
return fmt.Sprintf(ThresholdRangeTrigger, iops...), nil
}