diff --git a/plugins/outputs/pagerduty/README.md b/plugins/outputs/pagerduty/README.md new file mode 100644 index 000000000..940c38dba --- /dev/null +++ b/plugins/outputs/pagerduty/README.md @@ -0,0 +1,16 @@ +# PagerDuty output plugin + +This plugin is used to send PagerDuty alerts based on a metric field. + +Following is an example of send PagerDuty alerts based on "time_iowait" field +of the "cpu" metric. It will send PagerDyty alert any time the "time_iowait" +value is more than 50 + +```toml +[[outputs.pagerduty]] + service_key = "" + metric = "cpu" + description = "Check CPU" + field = "time_iowait" + expression = "> 50.0" +``` diff --git a/plugins/outputs/pagerduty/pagerduty.go b/plugins/outputs/pagerduty/pagerduty.go new file mode 100644 index 000000000..6328b5f31 --- /dev/null +++ b/plugins/outputs/pagerduty/pagerduty.go @@ -0,0 +1,114 @@ +package pagerduty + +import ( + "fmt" + pd "github.com/PagerDuty/go-pagerduty" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/outputs" + "go/token" + "go/types" + "log" +) + +type PD struct { + ServiceKey string `toml:"service_key"` + Desc string `toml:"description"` + Metric string `toml:"metric"` + Field string `toml:"field"` + Expression string `toml:"expression"` + Tags map[string]string `toml:"tags"` +} + +var sampleConfig = ` + ## PagerDuty service key + service_key = + ## Metric name that will be checked + metric = "cpu" + ## Description of the check + description = "Check CPU" + ## Name of the metric field which will be used to check + field = "time_iowait" + ## Expression is used to evaluate the alert + expression = "> 50.0" +` + +func (p *PD) Connect() error { + return nil +} + +func (p *PD) Close() error { + return nil +} + +func (p *PD) Match(metric telegraf.Metric) bool { + if p.Metric != metric.Name() { + log.Printf("Metric name is not matched. Expected: '%s' Found: '%s'", p.Metric, metric.Name()) + return false + } + for k, v := range p.Tags { + t, ok := metric.Tags()[k] + if !ok { + log.Printf("Tag value absent. Tag name: '%s'", k) + return false + } + if t != v { + log.Printf("Tag '%s' value not matched. Expected: '%s' Found: '%s'", k, v, t) + return false + } + } + field, ok := metric.Fields()[p.Field] + if !ok { + log.Printf("Filed '%s' absent", p.Field) + return false + } + expr := fmt.Sprintf("%v %s", field, p.Expression) + fs := token.NewFileSet() + tv, err := types.Eval(fs, nil, token.NoPos, expr) + if err != nil { + log.Printf("Error in parsing expression. Message:%s", err) + return false + } + return tv.Value.String() == "true" +} + +func (p *PD) SampleConfig() string { + return sampleConfig +} + +func (p *PD) Description() string { + return "Output metrics as PagerDuty event" +} + +func (p *PD) Write(metrics []telegraf.Metric) error { + if len(metrics) == 0 { + return nil + } + event := pd.Event{ + Type: "trigger", + ServiceKey: p.ServiceKey, + Description: p.Desc, + Client: "telegraf", + } + for _, metric := range metrics { + if !p.Match(metric) { + log.Println("Metric is not matched by threshold, skipping") + continue + } + m := make(map[string]interface{}) + m["tags"] = metric.Tags() + m["fields"] = metric.Fields() + m["name"] = metric.Name() + m["timestamp"] = metric.UnixNano() / 1000000000 + event.Details = m + _, err := pd.CreateEvent(event) + if err != nil { + return err + } + log.Println("Created PagerDuty event for metric: ", metric.Name()) + } + return nil +} + +func init() { + outputs.Add("pagerduty", func() telegraf.Output { return &PD{} }) +} diff --git a/plugins/outputs/pagerduty/pagerduty_test.go b/plugins/outputs/pagerduty/pagerduty_test.go new file mode 100644 index 000000000..9162520a0 --- /dev/null +++ b/plugins/outputs/pagerduty/pagerduty_test.go @@ -0,0 +1,26 @@ +package pagerduty + +import ( + "github.com/influxdata/telegraf/testutil" + "testing" +) + +func TestMetricMatch(t *testing.T) { + metric := testutil.TestMetric(1.0, "foo") + p := PD{ + Metric: "foo", + Field: "value", + Expression: "> 0", + } + if !p.Match(metric) { + t.Error("Metric did not match for greater than expression") + } + p.Expression = "== 1" + if !p.Match(metric) { + t.Error("Metric did not match for equality expression") + } + p.Expression = "< 0" + if p.Match(metric) { + t.Error("Metric did not match for less than expression") + } +}