108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
|
|
package monitoring
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
)
|
||
|
|
|
||
|
|
// HealthStatus 健康状态
|
||
|
|
type HealthStatus string
|
||
|
|
|
||
|
|
const (
|
||
|
|
HealthStatusUP HealthStatus = "UP"
|
||
|
|
HealthStatusDOWN HealthStatus = "DOWN"
|
||
|
|
HealthStatusUNKNOWN HealthStatus = "UNKNOWN"
|
||
|
|
)
|
||
|
|
|
||
|
|
// HealthCheck 健康检查器
|
||
|
|
type HealthCheck struct {
|
||
|
|
db *gorm.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewHealthCheck 创建健康检查器
|
||
|
|
func NewHealthCheck(db *gorm.DB) *HealthCheck {
|
||
|
|
return &HealthCheck{db: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Status 健康状态
|
||
|
|
type Status struct {
|
||
|
|
Status HealthStatus `json:"status"`
|
||
|
|
Checks map[string]CheckResult `json:"checks"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// CheckResult 检查结果
|
||
|
|
type CheckResult struct {
|
||
|
|
Status HealthStatus `json:"status"`
|
||
|
|
Error string `json:"error,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check 执行健康检查
|
||
|
|
func (h *HealthCheck) Check() *Status {
|
||
|
|
status := &Status{
|
||
|
|
Status: HealthStatusUP,
|
||
|
|
Checks: make(map[string]CheckResult),
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查数据库
|
||
|
|
dbResult := h.checkDatabase()
|
||
|
|
status.Checks["database"] = dbResult
|
||
|
|
if dbResult.Status != HealthStatusUP {
|
||
|
|
status.Status = HealthStatusDOWN
|
||
|
|
}
|
||
|
|
|
||
|
|
return status
|
||
|
|
}
|
||
|
|
|
||
|
|
// checkDatabase 检查数据库
|
||
|
|
func (h *HealthCheck) checkDatabase() CheckResult {
|
||
|
|
if h == nil || h.db == nil {
|
||
|
|
return CheckResult{
|
||
|
|
Status: HealthStatusDOWN,
|
||
|
|
Error: "database not configured",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sqlDB, err := h.db.DB()
|
||
|
|
if err != nil {
|
||
|
|
return CheckResult{
|
||
|
|
Status: HealthStatusDOWN,
|
||
|
|
Error: err.Error(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ping数据库
|
||
|
|
if err := sqlDB.Ping(); err != nil {
|
||
|
|
return CheckResult{
|
||
|
|
Status: HealthStatusDOWN,
|
||
|
|
Error: err.Error(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return CheckResult{Status: HealthStatusUP}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReadinessHandler reports dependency readiness.
|
||
|
|
func (h *HealthCheck) ReadinessHandler(c *gin.Context) {
|
||
|
|
status := h.Check()
|
||
|
|
|
||
|
|
httpStatus := http.StatusOK
|
||
|
|
if status.Status != HealthStatusUP {
|
||
|
|
httpStatus = http.StatusServiceUnavailable
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(httpStatus, status)
|
||
|
|
}
|
||
|
|
|
||
|
|
// LivenessHandler reports process liveness without dependency checks.
|
||
|
|
func (h *HealthCheck) LivenessHandler(c *gin.Context) {
|
||
|
|
c.Status(http.StatusNoContent)
|
||
|
|
c.Writer.WriteHeaderNow()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handler keeps backward compatibility with the historical /health endpoint.
|
||
|
|
func (h *HealthCheck) Handler(c *gin.Context) {
|
||
|
|
h.ReadinessHandler(c)
|
||
|
|
}
|