90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
package monitor
|
|
|
|
import (
|
|
"runtime"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var startedAt = time.Now()
|
|
|
|
var totalRequests uint64
|
|
var totalErrors uint64
|
|
|
|
type Snapshot struct {
|
|
UptimeSeconds int64 `json:"uptime_seconds"`
|
|
Goroutines int `json:"goroutines"`
|
|
MemoryAllocMB float64 `json:"memory_alloc_mb"`
|
|
MemorySysMB float64 `json:"memory_sys_mb"`
|
|
CPUCores int `json:"cpu_cores"`
|
|
DiskTotalGB float64 `json:"disk_total_gb"`
|
|
DiskFreeGB float64 `json:"disk_free_gb"`
|
|
RequestsTotal uint64 `json:"requests_total"`
|
|
ErrorsTotal uint64 `json:"errors_total"`
|
|
}
|
|
|
|
var (
|
|
cachedSnapshot Snapshot
|
|
snapshotMutex sync.RWMutex
|
|
)
|
|
|
|
func init() {
|
|
// Initialize the snapshot once on startup
|
|
updateSnapshot()
|
|
|
|
// Update the snapshot in the background every 2 seconds to avoid STW runtime.ReadMemStats in request threads
|
|
go func() {
|
|
ticker := time.NewTicker(2 * time.Second)
|
|
for range ticker.C {
|
|
updateSnapshot()
|
|
}
|
|
}()
|
|
}
|
|
|
|
func updateSnapshot() {
|
|
var mem runtime.MemStats
|
|
runtime.ReadMemStats(&mem)
|
|
|
|
diskTotal, diskFree := getDiskSpaceGB()
|
|
|
|
snapshotMutex.Lock()
|
|
cachedSnapshot = Snapshot{
|
|
UptimeSeconds: int64(time.Since(startedAt).Seconds()),
|
|
Goroutines: runtime.NumGoroutine(),
|
|
MemoryAllocMB: bytesToMB(mem.Alloc),
|
|
MemorySysMB: bytesToMB(mem.Sys),
|
|
CPUCores: runtime.NumCPU(),
|
|
DiskTotalGB: diskTotal,
|
|
DiskFreeGB: diskFree,
|
|
RequestsTotal: atomic.LoadUint64(&totalRequests),
|
|
ErrorsTotal: atomic.LoadUint64(&totalErrors),
|
|
}
|
|
snapshotMutex.Unlock()
|
|
}
|
|
|
|
func IncrementRequestCount() {
|
|
atomic.AddUint64(&totalRequests, 1)
|
|
}
|
|
|
|
func IncrementErrorCount() {
|
|
atomic.AddUint64(&totalErrors, 1)
|
|
}
|
|
|
|
func GetSnapshot() Snapshot {
|
|
snapshotMutex.RLock()
|
|
defer snapshotMutex.RUnlock()
|
|
|
|
// Return the cached snapshot, overlaying volatile/cheap fields in real-time
|
|
s := cachedSnapshot
|
|
s.UptimeSeconds = int64(time.Since(startedAt).Seconds())
|
|
s.Goroutines = runtime.NumGoroutine()
|
|
s.RequestsTotal = atomic.LoadUint64(&totalRequests)
|
|
s.ErrorsTotal = atomic.LoadUint64(&totalErrors)
|
|
return s
|
|
}
|
|
|
|
func bytesToMB(v uint64) float64 {
|
|
return float64(v) / 1024.0 / 1024.0
|
|
}
|