github.com/gofiber/fiber/v2@v2.47.0/middleware/monitor/monitor.go (about)

     1  package monitor
     2  
     3  import (
     4  	"os"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"github.com/gofiber/fiber/v2"
    10  	"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
    11  	"github.com/gofiber/fiber/v2/internal/gopsutil/load"
    12  	"github.com/gofiber/fiber/v2/internal/gopsutil/mem"
    13  	"github.com/gofiber/fiber/v2/internal/gopsutil/net"
    14  	"github.com/gofiber/fiber/v2/internal/gopsutil/process"
    15  )
    16  
    17  type stats struct {
    18  	PID statsPID `json:"pid"`
    19  	OS  statsOS  `json:"os"`
    20  }
    21  
    22  type statsPID struct {
    23  	CPU   float64 `json:"cpu"`
    24  	RAM   uint64  `json:"ram"`
    25  	Conns int     `json:"conns"`
    26  }
    27  
    28  type statsOS struct {
    29  	CPU      float64 `json:"cpu"`
    30  	RAM      uint64  `json:"ram"`
    31  	TotalRAM uint64  `json:"total_ram"`
    32  	LoadAvg  float64 `json:"load_avg"`
    33  	Conns    int     `json:"conns"`
    34  }
    35  
    36  var (
    37  	monitPIDCPU   atomic.Value
    38  	monitPIDRAM   atomic.Value
    39  	monitPIDConns atomic.Value
    40  
    41  	monitOSCPU      atomic.Value
    42  	monitOSRAM      atomic.Value
    43  	monitOSTotalRAM atomic.Value
    44  	monitOSLoadAvg  atomic.Value
    45  	monitOSConns    atomic.Value
    46  )
    47  
    48  var (
    49  	mutex sync.RWMutex
    50  	once  sync.Once
    51  	data  = &stats{}
    52  )
    53  
    54  // New creates a new middleware handler
    55  func New(config ...Config) fiber.Handler {
    56  	// Set default config
    57  	cfg := configDefault(config...)
    58  
    59  	// Start routine to update statistics
    60  	once.Do(func() {
    61  		p, _ := process.NewProcess(int32(os.Getpid())) //nolint:errcheck // TODO: Handle error
    62  
    63  		updateStatistics(p)
    64  
    65  		go func() {
    66  			for {
    67  				time.Sleep(cfg.Refresh)
    68  
    69  				updateStatistics(p)
    70  			}
    71  		}()
    72  	})
    73  
    74  	// Return new handler
    75  	//nolint:errcheck // Ignore the type-assertion errors
    76  	return func(c *fiber.Ctx) error {
    77  		// Don't execute middleware if Next returns true
    78  		if cfg.Next != nil && cfg.Next(c) {
    79  			return c.Next()
    80  		}
    81  
    82  		if c.Method() != fiber.MethodGet {
    83  			return fiber.ErrMethodNotAllowed
    84  		}
    85  		if c.Get(fiber.HeaderAccept) == fiber.MIMEApplicationJSON || cfg.APIOnly {
    86  			mutex.Lock()
    87  			data.PID.CPU, _ = monitPIDCPU.Load().(float64)
    88  			data.PID.RAM, _ = monitPIDRAM.Load().(uint64)
    89  			data.PID.Conns, _ = monitPIDConns.Load().(int)
    90  
    91  			data.OS.CPU, _ = monitOSCPU.Load().(float64)
    92  			data.OS.RAM, _ = monitOSRAM.Load().(uint64)
    93  			data.OS.TotalRAM, _ = monitOSTotalRAM.Load().(uint64)
    94  			data.OS.LoadAvg, _ = monitOSLoadAvg.Load().(float64)
    95  			data.OS.Conns, _ = monitOSConns.Load().(int)
    96  			mutex.Unlock()
    97  			return c.Status(fiber.StatusOK).JSON(data)
    98  		}
    99  		c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8)
   100  		return c.Status(fiber.StatusOK).SendString(cfg.index)
   101  	}
   102  }
   103  
   104  func updateStatistics(p *process.Process) {
   105  	pidCPU, err := p.CPUPercent()
   106  	if err == nil {
   107  		monitPIDCPU.Store(pidCPU / 10)
   108  	}
   109  
   110  	if osCPU, err := cpu.Percent(0, false); err == nil && len(osCPU) > 0 {
   111  		monitOSCPU.Store(osCPU[0])
   112  	}
   113  
   114  	if pidRAM, err := p.MemoryInfo(); err == nil && pidRAM != nil {
   115  		monitPIDRAM.Store(pidRAM.RSS)
   116  	}
   117  
   118  	if osRAM, err := mem.VirtualMemory(); err == nil && osRAM != nil {
   119  		monitOSRAM.Store(osRAM.Used)
   120  		monitOSTotalRAM.Store(osRAM.Total)
   121  	}
   122  
   123  	if loadAvg, err := load.Avg(); err == nil && loadAvg != nil {
   124  		monitOSLoadAvg.Store(loadAvg.Load1)
   125  	}
   126  
   127  	pidConns, err := net.ConnectionsPid("tcp", p.Pid)
   128  	if err == nil {
   129  		monitPIDConns.Store(len(pidConns))
   130  	}
   131  
   132  	osConns, err := net.Connections("tcp")
   133  	if err == nil {
   134  		monitOSConns.Store(len(osConns))
   135  	}
   136  }