Compare commits
2 Commits
main
...
http_healt
Author | SHA1 | Date |
---|---|---|
haiyux | c5292a62a1 | 2 years ago |
haiyux | 592f569517 | 2 years ago |
@ -0,0 +1,120 @@ |
|||||||
|
package health |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"encoding/json" |
||||||
|
"net/http" |
||||||
|
"sync" |
||||||
|
) |
||||||
|
|
||||||
|
type Status string |
||||||
|
|
||||||
|
const ( |
||||||
|
Up Status = "UP" |
||||||
|
Down Status = "DOWN" |
||||||
|
) |
||||||
|
|
||||||
|
type Checker interface { |
||||||
|
Check(ctx context.Context) error |
||||||
|
} |
||||||
|
|
||||||
|
type CheckerFunc func(ctx context.Context) error |
||||||
|
|
||||||
|
func (f CheckerFunc) Check(ctx context.Context) error { |
||||||
|
return f(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
type Health struct { |
||||||
|
lock sync.RWMutex |
||||||
|
status Status |
||||||
|
checkers map[string]Checker |
||||||
|
} |
||||||
|
|
||||||
|
func New() *Health { |
||||||
|
h := &Health{ |
||||||
|
lock: sync.RWMutex{}, |
||||||
|
status: Down, |
||||||
|
checkers: make(map[string]Checker), |
||||||
|
} |
||||||
|
return h |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Health) Register(name string, checker CheckerFunc) { |
||||||
|
h.lock.Lock() |
||||||
|
defer h.lock.Unlock() |
||||||
|
h.checkers[name] = checker |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Health) Start(_ context.Context) error { |
||||||
|
h.lock.Lock() |
||||||
|
defer h.lock.Unlock() |
||||||
|
h.status = Up |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Health) Stop(_ context.Context) error { |
||||||
|
h.lock.Lock() |
||||||
|
defer h.lock.Unlock() |
||||||
|
h.status = Down |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Health) Check(ctx context.Context) Result { |
||||||
|
h.lock.RLock() |
||||||
|
defer h.lock.RUnlock() |
||||||
|
res := Result{Status: h.status, Details: make(map[string]Detail, len(h.checkers))} |
||||||
|
for n, c := range h.checkers { |
||||||
|
if err := c.Check(ctx); err != nil { |
||||||
|
res.Status = Down |
||||||
|
res.Details[n] = Detail{Status: Down, Error: err.Error()} |
||||||
|
} else { |
||||||
|
res.Details[n] = Detail{Status: Up} |
||||||
|
} |
||||||
|
} |
||||||
|
return res |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Health) CheckService(ctx context.Context, svc string) Detail { |
||||||
|
h.lock.RLock() |
||||||
|
c, ok := h.checkers[svc] |
||||||
|
h.lock.RUnlock() |
||||||
|
if !ok { |
||||||
|
return Detail{Status: Down, Error: "service not find"} |
||||||
|
} |
||||||
|
err := c.Check(ctx) |
||||||
|
if err != nil { |
||||||
|
return Detail{Status: Down, Error: err.Error()} |
||||||
|
} |
||||||
|
return Detail{Status: Up} |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Health) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||||
|
service := r.URL.Query().Get("service") |
||||||
|
if service == "" { |
||||||
|
res := h.Check(r.Context()) |
||||||
|
if res.Status == Down { |
||||||
|
w.WriteHeader(http.StatusInternalServerError) |
||||||
|
} else { |
||||||
|
w.WriteHeader(http.StatusOK) |
||||||
|
} |
||||||
|
_ = json.NewEncoder(w).Encode(res) |
||||||
|
} else { |
||||||
|
detail := h.CheckService(r.Context(), service) |
||||||
|
if detail.Status == Down { |
||||||
|
w.WriteHeader(http.StatusInternalServerError) |
||||||
|
} else { |
||||||
|
w.WriteHeader(http.StatusOK) |
||||||
|
} |
||||||
|
_ = json.NewEncoder(w).Encode(detail) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type Result struct { |
||||||
|
Status Status `json:"status"` |
||||||
|
Details map[string]Detail `json:"details"` |
||||||
|
} |
||||||
|
|
||||||
|
type Detail struct { |
||||||
|
Status Status `json:"status"` |
||||||
|
Error string `json:"error"` |
||||||
|
} |
Loading…
Reference in new issue