Files
godoxy-yusing/internal/types/health.go
2025-10-26 16:38:08 +08:00

197 lines
4.8 KiB
Go

package types
import (
"encoding/json"
"fmt"
"net/url"
"strconv"
"time"
"github.com/bytedance/sonic"
strutils "github.com/yusing/goutils/strings"
"github.com/yusing/goutils/task"
)
type (
HealthStatus uint8
HealthCheckResult struct {
Healthy bool `json:"healthy"`
Detail string `json:"detail"`
Latency time.Duration `json:"latency"`
} // @name HealthCheckResult
WithHealthInfo interface {
Status() HealthStatus
Uptime() time.Duration
Latency() time.Duration
Detail() string
}
HealthMonitor interface {
task.TaskStarter
task.TaskFinisher
fmt.Stringer
WithHealthInfo
Name() string
json.Marshaler
}
HealthChecker interface {
CheckHealth() (result HealthCheckResult, err error)
URL() *url.URL
Config() *HealthCheckConfig
UpdateURL(url *url.URL)
}
HealthMonCheck interface {
HealthMonitor
HealthChecker
}
HealthJSON struct {
Name string `json:"name"`
Config *HealthCheckConfig `json:"config"`
Started int64 `json:"started"`
StartedStr string `json:"startedStr"`
Status string `json:"status"`
Uptime float64 `json:"uptime"`
UptimeStr string `json:"uptimeStr"`
Latency float64 `json:"latency"`
LatencyStr string `json:"latencyStr"`
LastSeen int64 `json:"lastSeen"`
LastSeenStr string `json:"lastSeenStr"`
Detail string `json:"detail"`
URL string `json:"url"`
Extra *HealthExtra `json:"extra,omitempty" extensions:"x-nullable"`
} // @name HealthJSON
HealthJSONRepr struct {
Name string
Config *HealthCheckConfig
Status HealthStatus
Started time.Time
Uptime time.Duration
Latency time.Duration
LastSeen time.Time
Detail string
URL *url.URL
Extra *HealthExtra
}
HealthExtra struct {
Config *LoadBalancerConfig `json:"config"`
Pool map[string]any `json:"pool"`
} // @name HealthExtra
)
const (
StatusUnknown HealthStatus = 0
StatusHealthy HealthStatus = (1 << iota)
StatusNapping
StatusStarting
StatusUnhealthy
StatusError
StatusUnknownStr = "unknown"
StatusHealthyStr = "healthy"
StatusNappingStr = "napping"
StatusStartingStr = "starting"
StatusUnhealthyStr = "unhealthy"
StatusErrorStr = "error"
NumStatuses int = iota - 1
HealthyMask = StatusHealthy | StatusNapping | StatusStarting
IdlingMask = StatusNapping | StatusStarting
)
var (
StatusHealthyStr2 = strconv.Itoa(int(StatusHealthy))
StatusNappingStr2 = strconv.Itoa(int(StatusNapping))
StatusStartingStr2 = strconv.Itoa(int(StatusStarting))
StatusUnhealthyStr2 = strconv.Itoa(int(StatusUnhealthy))
StatusErrorStr2 = strconv.Itoa(int(StatusError))
)
func NewHealthStatusFromString(s string) HealthStatus {
switch s {
case StatusHealthyStr, StatusHealthyStr2:
return StatusHealthy
case StatusUnhealthyStr, StatusUnhealthyStr2:
return StatusUnhealthy
case StatusNappingStr, StatusNappingStr2:
return StatusNapping
case StatusStartingStr, StatusStartingStr2:
return StatusStarting
case StatusErrorStr, StatusErrorStr2:
return StatusError
default:
return StatusUnknown
}
}
func (s HealthStatus) String() string {
switch s {
case StatusHealthy:
return StatusHealthyStr
case StatusUnhealthy:
return StatusUnhealthyStr
case StatusNapping:
return StatusNappingStr
case StatusStarting:
return StatusStartingStr
case StatusError:
return StatusErrorStr
default:
return StatusUnknownStr
}
}
func (s HealthStatus) Good() bool {
return s&HealthyMask != 0
}
func (s HealthStatus) Bad() bool {
return s&HealthyMask == 0
}
func (s HealthStatus) Idling() bool {
return s&IdlingMask != 0
}
func (s HealthStatus) MarshalJSON() ([]byte, error) {
return strconv.AppendQuote(nil, s.String()), nil
}
func (s *HealthStatus) UnmarshalJSON(data []byte) error {
var v string
if err := sonic.Unmarshal(data, &v); err != nil {
return fmt.Errorf("failed to unmarshal health status: %w", err)
}
*s = NewHealthStatusFromString(v)
return nil
}
func (jsonRepr *HealthJSONRepr) MarshalJSON() ([]byte, error) {
var url string
if jsonRepr.URL != nil {
url = jsonRepr.URL.String()
}
if url == "http://:0" {
url = ""
}
return sonic.Marshal(HealthJSON{
Name: jsonRepr.Name,
Config: jsonRepr.Config,
Started: jsonRepr.Started.Unix(),
StartedStr: strutils.FormatTime(jsonRepr.Started),
Status: jsonRepr.Status.String(),
Uptime: jsonRepr.Uptime.Seconds(),
UptimeStr: strutils.FormatDuration(jsonRepr.Uptime),
Latency: jsonRepr.Latency.Seconds(),
LatencyStr: strconv.Itoa(int(jsonRepr.Latency.Milliseconds())) + " ms",
LastSeen: jsonRepr.LastSeen.Unix(),
LastSeenStr: strutils.FormatLastSeen(jsonRepr.LastSeen),
Detail: jsonRepr.Detail,
URL: url,
Extra: jsonRepr.Extra,
})
}