mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"fotbal-club/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// HealthController handles health check endpoints
|
||||
type HealthController struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
// HealthResponse represents health check response
|
||||
type HealthResponse struct {
|
||||
Status string `json:"status"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Checks map[string]CheckResult `json:"checks"`
|
||||
System SystemInfo `json:"system,omitempty"`
|
||||
}
|
||||
|
||||
// CheckResult represents individual health check result
|
||||
type CheckResult struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Latency time.Duration `json:"latency_ms,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// SystemInfo represents system resource information
|
||||
type SystemInfo struct {
|
||||
Goroutines int `json:"goroutines"`
|
||||
MemoryAlloc uint64 `json:"memory_alloc_mb"`
|
||||
MemoryTotal uint64 `json:"memory_total_mb"`
|
||||
MemorySys uint64 `json:"memory_sys_mb"`
|
||||
NumCPU int `json:"num_cpu"`
|
||||
GoVersion string `json:"go_version"`
|
||||
}
|
||||
|
||||
// Liveness returns a simple liveness probe
|
||||
func (hc *HealthController) Liveness(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "alive",
|
||||
"timestamp": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// Readiness returns detailed readiness probe
|
||||
func (hc *HealthController) Readiness(c *gin.Context) {
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
checks := make(map[string]CheckResult)
|
||||
overallStatus := "healthy"
|
||||
|
||||
// Check database
|
||||
dbCheck := hc.checkDatabase(ctx)
|
||||
checks["database"] = dbCheck
|
||||
if dbCheck.Status != "healthy" {
|
||||
overallStatus = "unhealthy"
|
||||
}
|
||||
|
||||
// Check cache
|
||||
cacheCheck := hc.checkCache(ctx)
|
||||
checks["cache"] = cacheCheck
|
||||
if cacheCheck.Status != "degraded" && cacheCheck.Status != "healthy" {
|
||||
overallStatus = "unhealthy"
|
||||
}
|
||||
|
||||
// Check disk space
|
||||
diskCheck := hc.checkDiskSpace()
|
||||
checks["disk"] = diskCheck
|
||||
if diskCheck.Status != "healthy" {
|
||||
overallStatus = "degraded"
|
||||
}
|
||||
|
||||
response := HealthResponse{
|
||||
Status: overallStatus,
|
||||
Timestamp: time.Now(),
|
||||
Checks: checks,
|
||||
}
|
||||
|
||||
statusCode := http.StatusOK
|
||||
if overallStatus == "unhealthy" {
|
||||
statusCode = http.StatusServiceUnavailable
|
||||
}
|
||||
|
||||
c.JSON(statusCode, response)
|
||||
}
|
||||
|
||||
// Health returns comprehensive health information
|
||||
func (hc *HealthController) Health(c *gin.Context) {
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
checks := make(map[string]CheckResult)
|
||||
overallStatus := "healthy"
|
||||
|
||||
// All checks
|
||||
dbCheck := hc.checkDatabase(ctx)
|
||||
checks["database"] = dbCheck
|
||||
if dbCheck.Status != "healthy" {
|
||||
overallStatus = "unhealthy"
|
||||
}
|
||||
|
||||
cacheCheck := hc.checkCache(ctx)
|
||||
checks["cache"] = cacheCheck
|
||||
|
||||
diskCheck := hc.checkDiskSpace()
|
||||
checks["disk"] = diskCheck
|
||||
|
||||
// System info
|
||||
sysInfo := hc.getSystemInfo()
|
||||
|
||||
response := HealthResponse{
|
||||
Status: overallStatus,
|
||||
Timestamp: time.Now(),
|
||||
Version: "1.0.0", // Use actual version
|
||||
Checks: checks,
|
||||
System: sysInfo,
|
||||
}
|
||||
|
||||
statusCode := http.StatusOK
|
||||
if overallStatus == "unhealthy" {
|
||||
statusCode = http.StatusServiceUnavailable
|
||||
}
|
||||
|
||||
c.JSON(statusCode, response)
|
||||
}
|
||||
|
||||
// checkDatabase verifies database connectivity
|
||||
func (hc *HealthController) checkDatabase(ctx context.Context) CheckResult {
|
||||
start := time.Now()
|
||||
|
||||
sqlDB, err := hc.DB.DB()
|
||||
if err != nil {
|
||||
return CheckResult{
|
||||
Status: "unhealthy",
|
||||
Message: "Cannot get database connection: " + err.Error(),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
err = sqlDB.PingContext(ctx)
|
||||
latency := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
return CheckResult{
|
||||
Status: "unhealthy",
|
||||
Message: "Database ping failed: " + err.Error(),
|
||||
Latency: latency,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Check connection pool stats
|
||||
stats := sqlDB.Stats()
|
||||
if stats.OpenConnections >= stats.MaxOpenConnections {
|
||||
return CheckResult{
|
||||
Status: "degraded",
|
||||
Message: "Connection pool at maximum capacity",
|
||||
Latency: latency,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{
|
||||
Status: "healthy",
|
||||
Message: "Database connection successful",
|
||||
Latency: latency,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// checkCache verifies cache service
|
||||
func (hc *HealthController) checkCache(ctx context.Context) CheckResult {
|
||||
start := time.Now()
|
||||
cache := services.GetCacheService()
|
||||
|
||||
// Test cache write/read
|
||||
testKey := "health:check"
|
||||
testValue := "ok"
|
||||
|
||||
err := cache.Set(testKey, testValue, 1*time.Minute)
|
||||
if err != nil {
|
||||
return CheckResult{
|
||||
Status: "unhealthy",
|
||||
Message: "Cache write failed: " + err.Error(),
|
||||
Latency: time.Since(start),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
var result string
|
||||
err = cache.Get(testKey, &result)
|
||||
if err != nil {
|
||||
return CheckResult{
|
||||
Status: "unhealthy",
|
||||
Message: "Cache read failed: " + err.Error(),
|
||||
Latency: time.Since(start),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
cache.Delete(testKey)
|
||||
|
||||
return CheckResult{
|
||||
Status: "healthy",
|
||||
Message: "Cache operational",
|
||||
Latency: time.Since(start),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// checkDiskSpace checks available disk space
|
||||
func (hc *HealthController) checkDiskSpace() CheckResult {
|
||||
// This is a simplified check
|
||||
// In production, implement proper disk space monitoring
|
||||
|
||||
return CheckResult{
|
||||
Status: "healthy",
|
||||
Message: "Disk space sufficient",
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// getSystemInfo collects system resource information
|
||||
func (hc *HealthController) getSystemInfo() SystemInfo {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
return SystemInfo{
|
||||
Goroutines: runtime.NumGoroutine(),
|
||||
MemoryAlloc: m.Alloc / 1024 / 1024, // MB
|
||||
MemoryTotal: m.TotalAlloc / 1024 / 1024, // MB
|
||||
MemorySys: m.Sys / 1024 / 1024, // MB
|
||||
NumCPU: runtime.NumCPU(),
|
||||
GoVersion: runtime.Version(),
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics returns Prometheus-compatible metrics
|
||||
func (hc *HealthController) Metrics(c *gin.Context) {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
// Simple text-based metrics
|
||||
metrics := ""
|
||||
metrics += "# HELP go_goroutines Number of goroutines\n"
|
||||
metrics += "# TYPE go_goroutines gauge\n"
|
||||
metrics += "go_goroutines " + string(rune(runtime.NumGoroutine())) + "\n\n"
|
||||
|
||||
metrics += "# HELP go_memory_alloc_bytes Allocated memory in bytes\n"
|
||||
metrics += "# TYPE go_memory_alloc_bytes gauge\n"
|
||||
metrics += "go_memory_alloc_bytes " + string(rune(m.Alloc)) + "\n\n"
|
||||
|
||||
c.String(http.StatusOK, metrics)
|
||||
}
|
||||
Reference in New Issue
Block a user