2021-02-13 05:29:21 +01:00
|
|
|
package health
|
|
|
|
|
2021-11-16 02:11:13 +01:00
|
|
|
import (
|
2021-11-20 05:43:24 +01:00
|
|
|
"encoding/json"
|
2021-11-16 02:11:13 +01:00
|
|
|
"net/http"
|
|
|
|
"sync"
|
|
|
|
)
|
2021-02-13 05:29:21 +01:00
|
|
|
|
|
|
|
var (
|
|
|
|
handler = &healthHandler{
|
|
|
|
useJSON: false,
|
|
|
|
status: Up,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-04-11 07:39:47 +02:00
|
|
|
// responseBody is the body of the response returned by the health handler.
|
2021-11-20 05:43:24 +01:00
|
|
|
type responseBody struct {
|
|
|
|
Status string `json:"status"`
|
|
|
|
Reason string `json:"reason,omitempty"`
|
|
|
|
}
|
|
|
|
|
2021-10-08 03:28:04 +02:00
|
|
|
// healthHandler is the HTTP handler for serving the health endpoint
|
2021-02-13 05:29:21 +01:00
|
|
|
type healthHandler struct {
|
2022-04-11 07:39:47 +02:00
|
|
|
useJSON bool
|
2021-11-16 02:11:13 +01:00
|
|
|
|
2021-11-20 05:43:24 +01:00
|
|
|
status Status
|
|
|
|
reason string
|
|
|
|
|
|
|
|
mutex sync.RWMutex
|
2021-02-13 05:29:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// WithJSON configures whether the handler should output a response in JSON or in raw text
|
|
|
|
//
|
|
|
|
// Defaults to false
|
|
|
|
func (h *healthHandler) WithJSON(v bool) *healthHandler {
|
|
|
|
h.useJSON = v
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2021-10-08 03:28:04 +02:00
|
|
|
// ServeHTTP serves the HTTP request for the health handler
|
2021-11-16 02:11:13 +01:00
|
|
|
func (h *healthHandler) ServeHTTP(writer http.ResponseWriter, _ *http.Request) {
|
|
|
|
var statusCode int
|
2021-02-13 05:29:21 +01:00
|
|
|
var body []byte
|
2021-11-20 05:43:24 +01:00
|
|
|
h.mutex.RLock()
|
|
|
|
status, reason, useJSON := h.status, h.reason, h.useJSON
|
|
|
|
h.mutex.RUnlock()
|
|
|
|
if status == Up {
|
2021-11-16 02:11:13 +01:00
|
|
|
statusCode = http.StatusOK
|
2021-02-13 05:29:21 +01:00
|
|
|
} else {
|
2021-11-16 02:11:13 +01:00
|
|
|
statusCode = http.StatusInternalServerError
|
2021-02-13 05:29:21 +01:00
|
|
|
}
|
2021-11-20 05:43:24 +01:00
|
|
|
if useJSON {
|
|
|
|
// We can safely ignore the error here because we know that both values are strings, therefore are supported encoders.
|
|
|
|
body, _ = json.Marshal(responseBody{Status: string(status), Reason: reason})
|
2021-02-13 05:29:21 +01:00
|
|
|
writer.Header().Set("Content-Type", "application/json")
|
|
|
|
} else {
|
2021-11-20 05:43:24 +01:00
|
|
|
if len(reason) == 0 {
|
|
|
|
body = []byte(status)
|
|
|
|
} else {
|
|
|
|
body = []byte(string(status) + ": " + reason)
|
|
|
|
}
|
2021-02-13 05:29:21 +01:00
|
|
|
}
|
2021-11-16 02:11:13 +01:00
|
|
|
writer.WriteHeader(statusCode)
|
2021-02-13 05:29:21 +01:00
|
|
|
_, _ = writer.Write(body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handler retrieves the health handler
|
|
|
|
func Handler() *healthHandler {
|
|
|
|
return handler
|
|
|
|
}
|
|
|
|
|
2021-11-16 02:11:13 +01:00
|
|
|
// GetStatus retrieves the current status returned by the health handler
|
|
|
|
func GetStatus() Status {
|
2021-11-20 05:43:24 +01:00
|
|
|
handler.mutex.RLock()
|
|
|
|
defer handler.mutex.RUnlock()
|
|
|
|
return handler.status
|
2021-11-16 02:11:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetStatus sets the status to be returned by the health handler
|
2021-02-13 05:29:21 +01:00
|
|
|
func SetStatus(status Status) {
|
2021-11-20 05:43:24 +01:00
|
|
|
handler.mutex.Lock()
|
|
|
|
handler.status = status
|
|
|
|
handler.mutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetReason retrieves the current status returned by the health handler
|
|
|
|
func GetReason() string {
|
|
|
|
handler.mutex.RLock()
|
|
|
|
defer handler.mutex.RUnlock()
|
|
|
|
return handler.reason
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetReason sets a reason for the current status to be returned by the health handler
|
|
|
|
func SetReason(reason string) {
|
|
|
|
handler.mutex.Lock()
|
|
|
|
handler.reason = reason
|
|
|
|
handler.mutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetStatusAndReason sets the status and reason to be returned by the health handler
|
|
|
|
func SetStatusAndReason(status Status, reason string) {
|
|
|
|
handler.mutex.Lock()
|
|
|
|
handler.status = status
|
|
|
|
handler.reason = reason
|
|
|
|
handler.mutex.Unlock()
|
2021-02-13 05:29:21 +01:00
|
|
|
}
|
2022-04-11 07:39:47 +02:00
|
|
|
|
|
|
|
// SetHealthy sets the status to Up and the reason to a blank string
|
|
|
|
func SetHealthy() {
|
|
|
|
handler.mutex.Lock()
|
|
|
|
handler.status = Up
|
|
|
|
handler.reason = ""
|
|
|
|
handler.mutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetUnhealthy sets the status to Down and the reason to the string passed as parameter
|
|
|
|
//
|
|
|
|
// Unlike SetHealthy, this function enforces setting a reason, because it's good practice to give at least a bit
|
|
|
|
// of information as to why an application is unhealthy, and this library attempts to promote good practices.
|
|
|
|
func SetUnhealthy(reason string) {
|
|
|
|
handler.mutex.Lock()
|
|
|
|
handler.status = Down
|
|
|
|
handler.reason = reason
|
|
|
|
handler.mutex.Unlock()
|
|
|
|
}
|