-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobservability.go
More file actions
127 lines (103 loc) · 2.55 KB
/
observability.go
File metadata and controls
127 lines (103 loc) · 2.55 KB
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 needle
import (
"context"
"sync"
"time"
)
type ResolveHook func(key string, duration time.Duration, err error)
type ProvideHook func(key string)
type StartHook func(key string, duration time.Duration, err error)
type StopHook func(key string, duration time.Duration, err error)
type HealthStatus string
const (
HealthStatusUp HealthStatus = "up"
HealthStatusDown HealthStatus = "down"
HealthStatusUnknown HealthStatus = "unknown"
)
type HealthReport struct {
Name string
Status HealthStatus
Error error
Latency time.Duration
}
type HealthChecker interface {
HealthCheck(ctx context.Context) error
}
type ReadinessChecker interface {
ReadinessCheck(ctx context.Context) error
}
func (c *Container) Live(ctx context.Context) error {
reports := c.checkHealth(ctx)
for _, r := range reports {
if r.Status == HealthStatusDown {
return errHealthCheckFailed(r.Name, r.Error)
}
}
return nil
}
func (c *Container) Ready(ctx context.Context) error {
reports := c.checkReadiness(ctx)
for _, r := range reports {
if r.Status == HealthStatusDown {
return errHealthCheckFailed(r.Name, r.Error)
}
}
return nil
}
func (c *Container) Health(ctx context.Context) []HealthReport {
return c.checkHealth(ctx)
}
func (c *Container) checkHealth(ctx context.Context) []HealthReport {
return c.runChecks(ctx, func(instance any) func(context.Context) error {
if hc, ok := instance.(HealthChecker); ok {
return hc.HealthCheck
}
return nil
})
}
func (c *Container) checkReadiness(ctx context.Context) []HealthReport {
return c.runChecks(ctx, func(instance any) func(context.Context) error {
if rc, ok := instance.(ReadinessChecker); ok {
return rc.ReadinessCheck
}
return nil
})
}
func (c *Container) runChecks(ctx context.Context, extractCheck func(any) func(context.Context) error) []HealthReport {
keys := c.internal.Keys()
var reports []HealthReport
var mu sync.Mutex
var wg sync.WaitGroup
for _, key := range keys {
instance, ok := c.internal.GetInstance(key)
if !ok {
continue
}
check := extractCheck(instance)
if check == nil {
continue
}
wg.Add(1)
go func(k string, fn func(context.Context) error) {
defer wg.Done()
start := time.Now()
err := fn(ctx)
latency := time.Since(start)
report := HealthReport{
Name: k,
Latency: latency,
}
if err != nil {
report.Status = HealthStatusDown
report.Error = err
} else {
report.Status = HealthStatusUp
}
mu.Lock()
reports = append(reports, report)
mu.Unlock()
}(key, check)
}
wg.Wait()
return reports
}