fiber/middleware/monitor/monitor.go

137 lines
3.2 KiB
Go

package monitor
import (
"os"
"sync"
"sync/atomic"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
"github.com/gofiber/fiber/v2/internal/gopsutil/load"
"github.com/gofiber/fiber/v2/internal/gopsutil/mem"
"github.com/gofiber/fiber/v2/internal/gopsutil/net"
"github.com/gofiber/fiber/v2/internal/gopsutil/process"
)
type stats struct {
PID statsPID `json:"pid"`
OS statsOS `json:"os"`
}
type statsPID struct {
CPU float64 `json:"cpu"`
RAM uint64 `json:"ram"`
Conns int `json:"conns"`
}
type statsOS struct {
CPU float64 `json:"cpu"`
RAM uint64 `json:"ram"`
TotalRAM uint64 `json:"total_ram"`
LoadAvg float64 `json:"load_avg"`
Conns int `json:"conns"`
}
var (
monitPIDCPU atomic.Value
monitPIDRAM atomic.Value
monitPIDConns atomic.Value
monitOSCPU atomic.Value
monitOSRAM atomic.Value
monitOSTotalRAM atomic.Value
monitOSLoadAvg atomic.Value
monitOSConns atomic.Value
)
var (
mutex sync.RWMutex
once sync.Once
data = &stats{}
)
// New creates a new middleware handler
func New(config ...Config) fiber.Handler {
// Set default config
cfg := configDefault(config...)
// Start routine to update statistics
once.Do(func() {
p, _ := process.NewProcess(int32(os.Getpid())) //nolint:errcheck // TODO: Handle error
updateStatistics(p)
go func() {
for {
time.Sleep(cfg.Refresh)
updateStatistics(p)
}
}()
})
// Return new handler
//nolint:errcheck // Ignore the type-assertion errors
return func(c *fiber.Ctx) error {
// Don't execute middleware if Next returns true
if cfg.Next != nil && cfg.Next(c) {
return c.Next()
}
if c.Method() != fiber.MethodGet {
return fiber.ErrMethodNotAllowed
}
if c.Get(fiber.HeaderAccept) == fiber.MIMEApplicationJSON || cfg.APIOnly {
mutex.Lock()
data.PID.CPU, _ = monitPIDCPU.Load().(float64)
data.PID.RAM, _ = monitPIDRAM.Load().(uint64)
data.PID.Conns, _ = monitPIDConns.Load().(int)
data.OS.CPU, _ = monitOSCPU.Load().(float64)
data.OS.RAM, _ = monitOSRAM.Load().(uint64)
data.OS.TotalRAM, _ = monitOSTotalRAM.Load().(uint64)
data.OS.LoadAvg, _ = monitOSLoadAvg.Load().(float64)
data.OS.Conns, _ = monitOSConns.Load().(int)
mutex.Unlock()
return c.Status(fiber.StatusOK).JSON(data)
}
c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8)
return c.Status(fiber.StatusOK).SendString(cfg.index)
}
}
func updateStatistics(p *process.Process) {
pidCPU, err := p.CPUPercent()
if err == nil {
monitPIDCPU.Store(pidCPU / 10)
}
if osCPU, err := cpu.Percent(0, false); err == nil && len(osCPU) > 0 {
monitOSCPU.Store(osCPU[0])
}
if pidRAM, err := p.MemoryInfo(); err == nil && pidRAM != nil {
monitPIDRAM.Store(pidRAM.RSS)
}
if osRAM, err := mem.VirtualMemory(); err == nil && osRAM != nil {
monitOSRAM.Store(osRAM.Used)
monitOSTotalRAM.Store(osRAM.Total)
}
if loadAvg, err := load.Avg(); err == nil && loadAvg != nil {
monitOSLoadAvg.Store(loadAvg.Load1)
}
pidConns, err := net.ConnectionsPid("tcp", p.Pid)
if err == nil {
monitPIDConns.Store(len(pidConns))
}
osConns, err := net.Connections("tcp")
if err == nil {
monitOSConns.Store(len(osConns))
}
}