forked from Whyeasy/prometheus-statuspage-pusher
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmain.go
127 lines (105 loc) · 3.22 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
"github.com/prometheus/common/model"
)
var (
prometheusURL = flag.String("prom", "http://localhost:9090", "URL of Prometheus server")
statusPageAPIKey = flag.String("apikey", "", "Statuspage API key")
statusPageID = flag.String("pageid", "", "Statuspage page ID")
queryConfigFile = flag.String("config", "queries.yaml", "Query config file")
metricInterval = flag.Duration("interval", 30*time.Second, "Metric push interval")
metricValueRounding = flag.Uint("rounding", 6, "Round metric values to specific decimal places")
backfillDuration = flag.String("backfill", "", "Backfill the data points in, for example, 5d")
logLevel = flag.String("log-level", "info", "Log level accepted by Logrus, for example, \"error\", \"warn\", \"info\", \"debug\", ...")
httpClient = &http.Client{
Timeout: 30 * time.Second,
}
queryConfig map[string]string
)
func main() {
flag.Parse()
if lvl, err := log.ParseLevel(*logLevel); err != nil {
log.Fatal(err)
} else {
log.SetLevel(lvl)
}
qcd, err := ioutil.ReadFile(*queryConfigFile)
if err != nil {
log.Fatalf("Couldn't read config file: %s", err)
}
if err := yaml.Unmarshal(qcd, &queryConfig); err != nil {
log.Fatalf("Couldn't parse config file: %s", err)
}
if *backfillDuration != "" {
md, err := model.ParseDuration(*backfillDuration)
if err != nil {
log.Fatalf("Incorrect duration format: %s", *backfillDuration)
}
d := time.Duration(md)
queryAndPush(&d)
} else {
queryAndPush(nil)
}
ticker := time.NewTicker(*metricInterval)
for {
select {
case <-ticker.C:
go queryAndPush(nil)
}
}
}
func queryAndPush(backfill *time.Duration) {
log.Infof("Started to query and pushing metrics")
metrics := queryPrometheus(backfill, *metricValueRounding)
chunkedMetrics := chunkMetrics(metrics)
for _, m := range chunkedMetrics {
err := pushStatuspage(m)
if err != nil {
log.Error(err)
}
}
log.Infof("Finished querying and pushing metrics")
}
func pushStatuspage(metrics statuspageMetrics) error {
jsonContents, err := json.Marshal(statuspageMetricsPayload{Data: metrics})
if err != nil {
return err
}
log.Debugf("Metrics payload pushing to Statuspage: %s", jsonContents)
metricIDs := make([]string, 0, len(metrics))
for id := range metrics {
metricIDs = append(metricIDs, id)
}
log.Infof("Pushing metrics: %s", strings.Join(metricIDs, ", "))
url := fmt.Sprintf("https://api.statuspage.io/v1/pages/%s/metrics/data", url.PathEscape(*statusPageID))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonContents))
if err != nil {
return err
}
req.Header.Set("Authorization", "OAuth "+*statusPageAPIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
respStr, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("HTTP status %d, Empty API response", resp.StatusCode)
}
return fmt.Errorf("HTTP status %d, API error: %s", resp.StatusCode, string(respStr))
}
return nil
}