forked from TykTechnologies/tyk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api_healthcheck.go
144 lines (119 loc) · 4.57 KB
/
api_healthcheck.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package main
import (
"strconv"
"strings"
"time"
)
type HealthPrefix string
const (
Throttle HealthPrefix = "Throttle"
QuotaViolation HealthPrefix = "QuotaViolation"
KeyFailure HealthPrefix = "KeyFailure"
RequestLog HealthPrefix = "Request"
BlockedRequestLog HealthPrefix = "BlockedRequest"
HealthCheckRedisPrefix string = "apihealth"
)
type HealthChecker interface {
Init(StorageHandler)
GetApiHealthValues() (HealthCheckValues, error)
StoreCounterVal(HealthPrefix, string)
}
type HealthCheckValues struct {
ThrottledRequestsPS float64 `bson:"throttle_reqests_per_second,omitempty" json:"throttle_reqests_per_second"`
QuotaViolationsPS float64 `bson:"quota_violations_per_second,omitempty" json:"quota_violations_per_second"`
KeyFailuresPS float64 `bson:"key_failures_per_second,omitempty" json:"key_failures_per_second"`
AvgUpstreamLatency float64 `bson:"average_upstream_latency,omitempty" json:"average_upstream_latency"`
AvgRequestsPS float64 `bson:"average_requests_per_second,omitempty" json:"average_requests_per_second"`
}
type DefaultHealthChecker struct {
storage StorageHandler
APIID string
}
func (h *DefaultHealthChecker) Init(storeType StorageHandler) {
if config.HealthCheck.EnableHealthChecks {
log.Debug("Health Checker initialised.")
}
h.storage = storeType
h.storage.Connect()
}
func (h *DefaultHealthChecker) CreateKeyName(subKey HealthPrefix) string {
var newKey string
//now := time.Now().UnixNano()
// Key should be API-ID.SubKey.123456789
//newKey = strings.Join([]string{h.APIID, string(subKey), strconv.FormatInt(now, 10)}, ".")
newKey = strings.Join([]string{h.APIID, string(subKey)}, ".")
return newKey
}
// ReportHealthCheckValue is a shortcut we can use throughout the app to push a health check value
func ReportHealthCheckValue(checker HealthChecker, counter HealthPrefix, value string) {
// TODO: Wrap this in a conditional so it can be deactivated
go checker.StoreCounterVal(counter, value)
}
func (h *DefaultHealthChecker) StoreCounterVal(counterType HealthPrefix, value string) {
if config.HealthCheck.EnableHealthChecks {
searchStr := h.CreateKeyName(counterType)
log.Debug("Adding Healthcheck to: ", searchStr)
log.Debug("Val is: ", value)
//go h.storage.SetKey(searchStr, value, config.HealthCheck.HealthCheckValueTimeout)
if value != "-1" {
// need to ensure uniqueness
now_string := strconv.Itoa(int(time.Now().UnixNano()))
value = now_string + "." + value
log.Debug("Set value to: ", value)
}
go h.storage.SetRollingWindow(searchStr, config.HealthCheck.HealthCheckValueTimeout, value)
}
}
func (h *DefaultHealthChecker) getAvgCount(prefix HealthPrefix) float64 {
//searchStr := strings.Join([]string{h.APIID, string(prefix)}, ".")
searchStr := h.CreateKeyName(prefix)
log.Debug("Searching for: ", searchStr)
var count int
count, _ = h.storage.SetRollingWindow(searchStr, config.HealthCheck.HealthCheckValueTimeout, "-1")
log.Debug("Count is: ", count)
//count = int64(len(keys))
divisor := float64(config.HealthCheck.HealthCheckValueTimeout)
if divisor == 0 {
log.Warning("The Health Check sample timeout is set to 0, samples will never be deleted!!!")
divisor = 60.0
}
if count > 0 {
return roundValue((float64(count) - 1) / divisor)
}
return 0.00
}
func roundValue(untruncated float64) float64 {
truncated := float64(int(untruncated*100)) / 100
return truncated
}
func (h *DefaultHealthChecker) GetApiHealthValues() (HealthCheckValues, error) {
values := HealthCheckValues{}
// Get the counted / average values
values.ThrottledRequestsPS = h.getAvgCount(Throttle)
values.QuotaViolationsPS = h.getAvgCount(QuotaViolation)
values.KeyFailuresPS = h.getAvgCount(KeyFailure)
values.AvgRequestsPS = h.getAvgCount(RequestLog)
// Get the micro latency graph, an average upstream latency
searchStr := strings.Join([]string{h.APIID, string(RequestLog)}, ".")
log.Debug("Searching KV for: ", searchStr)
//kv := h.storage.GetKeysAndValuesWithFilter(searchStr)
_, vals := h.storage.SetRollingWindow(searchStr, config.HealthCheck.HealthCheckValueTimeout, "-1")
log.Debug("Found: ", vals)
var runningTotal int
if len(vals) > 0 {
for _, v := range vals {
log.Debug("V is: ", string(v.([]byte)))
splitValues := strings.Split(string(v.([]byte)), ".")
if len(splitValues) > 1 {
vInt, cErr := strconv.Atoi(splitValues[1])
if cErr != nil {
log.Error("Couldn't convert tracked latency value to Int, vl is: ", cErr)
} else {
runningTotal += vInt
}
}
}
values.AvgUpstreamLatency = roundValue(float64(runningTotal / len(vals)))
}
return values, nil
}