-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathprometheus.go
125 lines (105 loc) · 2.55 KB
/
prometheus.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
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strings"
"time"
"github.com/fatih/color"
)
type promResponse struct {
Status string `json:"status"`
Data struct {
ResultType string `json:"resultType"`
Result []struct {
Metric map[string]string `json:"metric"`
Values [][]interface{} `json:"values"`
} `json:"result"`
} `json:"data"`
}
type Result struct {
Metric string
Values map[string]string
}
func Query(host string, start time.Time, end time.Time,
resolution time.Duration, query string) ([]Result, error) {
u, err := url.Parse(host)
if err != nil {
return nil, err
}
api_path, err := url.Parse("api/v1/query_range")
if err != nil {
return nil, err
}
u = u.ResolveReference(api_path)
q := u.Query()
q.Set("query", query)
q.Set("start", fmt.Sprintf("%d", start.Unix()))
q.Set("end", fmt.Sprintf("%d", end.Unix()))
q.Set("step", fmt.Sprintf("%d", steps(end.Sub(start), resolution)))
u.RawQuery = q.Encode()
response, err := http.Get(u.String())
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != 200 {
return nil, fmt.Errorf("didn't return 200 OK but %s: %s", response.Status, u.String())
}
var resp promResponse
if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
return nil, err
}
if resp.Data.ResultType != "matrix" {
return nil, fmt.Errorf("result type isn't of type matrix: %s", resp.Data.ResultType)
}
if len(resp.Data.Result) == 0 {
return nil, fmt.Errorf(color.YellowString("no timeseries found"))
}
var results []Result
for _, res := range resp.Data.Result {
r := Result{}
r.Metric = metricName(res.Metric)
values := make(map[string]string)
for _, vals := range res.Values {
timestamp := vals[0].(float64)
value := vals[1].(string)
values[fmt.Sprintf("%.f", timestamp)] = value
}
r.Values = values
results = append(results, r)
}
return results, nil
}
func steps(dur time.Duration, resolution time.Duration) int {
if dur < resolution {
return int(dur.Seconds())
}
if resolution <= time.Second {
resolution = time.Second
}
return int(resolution.Seconds())
}
func metricName(metric map[string]string) string {
if len(metric) == 0 {
return "{}"
}
out := ""
var inner []string
for key, value := range metric {
if key == "__name__" {
out = value
continue
}
inner = append(inner, fmt.Sprintf(`%s="%s"`, key, value))
}
if len(inner) == 0 {
return out
}
sort.Slice(inner, func(i, j int) bool {
return inner[i] < inner[j]
})
return out + "{" + strings.Join(inner, ",") + "}"
}