analytic.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package api
  2. import (
  3. "fmt"
  4. "github.com/0xJacky/Nginx-UI/logger"
  5. "github.com/0xJacky/Nginx-UI/server/internal/analytic"
  6. "github.com/pkg/errors"
  7. "github.com/shirou/gopsutil/v3/cpu"
  8. "github.com/shirou/gopsutil/v3/disk"
  9. "github.com/shirou/gopsutil/v3/host"
  10. "github.com/shirou/gopsutil/v3/load"
  11. "github.com/shirou/gopsutil/v3/mem"
  12. "github.com/shirou/gopsutil/v3/net"
  13. "github.com/spf13/cast"
  14. "math"
  15. "net/http"
  16. "runtime"
  17. "time"
  18. "github.com/dustin/go-humanize"
  19. "github.com/gin-gonic/gin"
  20. "github.com/gorilla/websocket"
  21. )
  22. type CPUStat struct {
  23. User float64 `json:"user"`
  24. System float64 `json:"system"`
  25. Idle float64 `json:"idle"`
  26. Total float64 `json:"total"`
  27. }
  28. type MemStat struct {
  29. Total string `json:"total"`
  30. Used string `json:"used"`
  31. Cached string `json:"cached"`
  32. Free string `json:"free"`
  33. SwapUsed string `json:"swap_used"`
  34. SwapTotal string `json:"swap_total"`
  35. SwapCached string `json:"swap_cached"`
  36. SwapPercent float64 `json:"swap_percent"`
  37. Pressure float64 `json:"pressure"`
  38. }
  39. type DiskStat struct {
  40. Total string `json:"total"`
  41. Used string `json:"used"`
  42. Percentage float64 `json:"percentage"`
  43. Writes analytic.Usage `json:"writes"`
  44. Reads analytic.Usage `json:"reads"`
  45. }
  46. type Stat struct {
  47. Uptime uint64 `json:"uptime"`
  48. LoadAvg *load.AvgStat `json:"loadavg"`
  49. CPU CPUStat `json:"cpu"`
  50. Memory MemStat `json:"memory"`
  51. Disk DiskStat `json:"disk"`
  52. Network net.IOCountersStat `json:"network"`
  53. }
  54. func getMemoryStat() (MemStat, error) {
  55. memoryStat, err := mem.VirtualMemory()
  56. if err != nil {
  57. return MemStat{}, errors.Wrap(err, "error analytic getMemoryStat")
  58. }
  59. return MemStat{
  60. Total: humanize.Bytes(memoryStat.Total),
  61. Used: humanize.Bytes(memoryStat.Used),
  62. Cached: humanize.Bytes(memoryStat.Cached),
  63. Free: humanize.Bytes(memoryStat.Free),
  64. SwapUsed: humanize.Bytes(memoryStat.SwapTotal - memoryStat.SwapFree),
  65. SwapTotal: humanize.Bytes(memoryStat.SwapTotal),
  66. SwapCached: humanize.Bytes(memoryStat.SwapCached),
  67. SwapPercent: cast.ToFloat64(fmt.Sprintf("%.2f",
  68. 100*float64(memoryStat.SwapTotal-memoryStat.SwapFree)/math.Max(float64(memoryStat.SwapTotal), 1))),
  69. Pressure: cast.ToFloat64(fmt.Sprintf("%.2f", memoryStat.UsedPercent)),
  70. }, nil
  71. }
  72. func getDiskStat() (DiskStat, error) {
  73. diskUsage, err := disk.Usage(".")
  74. if err != nil {
  75. return DiskStat{}, errors.Wrap(err, "error analytic getDiskStat")
  76. }
  77. return DiskStat{
  78. Used: humanize.Bytes(diskUsage.Used),
  79. Total: humanize.Bytes(diskUsage.Total),
  80. Percentage: cast.ToFloat64(fmt.Sprintf("%.2f", diskUsage.UsedPercent)),
  81. Writes: analytic.DiskWriteRecord[len(analytic.DiskWriteRecord)-1],
  82. Reads: analytic.DiskReadRecord[len(analytic.DiskReadRecord)-1],
  83. }, nil
  84. }
  85. func Analytic(c *gin.Context) {
  86. var upGrader = websocket.Upgrader{
  87. CheckOrigin: func(r *http.Request) bool {
  88. return true
  89. },
  90. }
  91. // upgrade http to websocket
  92. ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
  93. if err != nil {
  94. logger.Error(err)
  95. return
  96. }
  97. defer ws.Close()
  98. var stat Stat
  99. for {
  100. stat.Memory, err = getMemoryStat()
  101. if err != nil {
  102. logger.Error(err)
  103. return
  104. }
  105. cpuTimesBefore, _ := cpu.Times(false)
  106. time.Sleep(1000 * time.Millisecond)
  107. cpuTimesAfter, _ := cpu.Times(false)
  108. threadNum := runtime.GOMAXPROCS(0)
  109. cpuUserUsage := (cpuTimesAfter[0].User - cpuTimesBefore[0].User) / (float64(1000*threadNum) / 1000)
  110. cpuSystemUsage := (cpuTimesAfter[0].System - cpuTimesBefore[0].System) / (float64(1000*threadNum) / 1000)
  111. stat.CPU = CPUStat{
  112. User: cast.ToFloat64(fmt.Sprintf("%.2f", cpuUserUsage*100)),
  113. System: cast.ToFloat64(fmt.Sprintf("%.2f", cpuSystemUsage*100)),
  114. Idle: cast.ToFloat64(fmt.Sprintf("%.2f", (1-cpuUserUsage-cpuSystemUsage)*100)),
  115. Total: cast.ToFloat64(fmt.Sprintf("%.2f", (cpuUserUsage+cpuSystemUsage)*100)),
  116. }
  117. stat.Uptime, _ = host.Uptime()
  118. stat.LoadAvg, _ = load.Avg()
  119. stat.Disk, err = getDiskStat()
  120. if err != nil {
  121. logger.Error(err)
  122. return
  123. }
  124. network, _ := net.IOCounters(false)
  125. if len(network) > 0 {
  126. stat.Network = network[0]
  127. }
  128. // write
  129. err = ws.WriteJSON(stat)
  130. if err != nil {
  131. logger.Error(err)
  132. break
  133. }
  134. time.Sleep(800 * time.Microsecond)
  135. }
  136. }
  137. func GetAnalyticInit(c *gin.Context) {
  138. cpuInfo, _ := cpu.Info()
  139. network, _ := net.IOCounters(false)
  140. memory, err := getMemoryStat()
  141. if err != nil {
  142. logger.Error(err)
  143. return
  144. }
  145. diskStat, err := getDiskStat()
  146. if err != nil {
  147. logger.Error(err)
  148. return
  149. }
  150. var _net net.IOCountersStat
  151. if len(network) > 0 {
  152. _net = network[0]
  153. }
  154. hostInfo, _ := host.Info()
  155. loadAvg, _ := load.Avg()
  156. c.JSON(http.StatusOK, gin.H{
  157. "host": hostInfo,
  158. "cpu": gin.H{
  159. "info": cpuInfo,
  160. "user": analytic.CpuUserRecord,
  161. "total": analytic.CpuTotalRecord,
  162. },
  163. "network": gin.H{
  164. "init": _net,
  165. "bytesRecv": analytic.NetRecvRecord,
  166. "bytesSent": analytic.NetSentRecord,
  167. },
  168. "disk_io": gin.H{
  169. "writes": analytic.DiskWriteRecord,
  170. "reads": analytic.DiskReadRecord,
  171. },
  172. "memory": memory,
  173. "disk": diskStat,
  174. "loadavg": loadAvg,
  175. })
  176. }