You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

admin.go 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package admin
  5. import (
  6. "fmt"
  7. "net/http"
  8. "runtime"
  9. "sort"
  10. "time"
  11. activities_model "code.gitea.io/gitea/models/activities"
  12. "code.gitea.io/gitea/modules/base"
  13. "code.gitea.io/gitea/modules/context"
  14. "code.gitea.io/gitea/modules/graceful"
  15. "code.gitea.io/gitea/modules/json"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/setting"
  18. "code.gitea.io/gitea/modules/updatechecker"
  19. "code.gitea.io/gitea/modules/web"
  20. "code.gitea.io/gitea/services/cron"
  21. "code.gitea.io/gitea/services/forms"
  22. repo_service "code.gitea.io/gitea/services/repository"
  23. )
  24. const (
  25. tplDashboard base.TplName = "admin/dashboard"
  26. tplCron base.TplName = "admin/cron"
  27. tplQueue base.TplName = "admin/queue"
  28. tplStacktrace base.TplName = "admin/stacktrace"
  29. tplQueueManage base.TplName = "admin/queue_manage"
  30. tplStats base.TplName = "admin/stats"
  31. )
  32. var sysStatus struct {
  33. StartTime string
  34. NumGoroutine int
  35. // General statistics.
  36. MemAllocated string // bytes allocated and still in use
  37. MemTotal string // bytes allocated (even if freed)
  38. MemSys string // bytes obtained from system (sum of XxxSys below)
  39. Lookups uint64 // number of pointer lookups
  40. MemMallocs uint64 // number of mallocs
  41. MemFrees uint64 // number of frees
  42. // Main allocation heap statistics.
  43. HeapAlloc string // bytes allocated and still in use
  44. HeapSys string // bytes obtained from system
  45. HeapIdle string // bytes in idle spans
  46. HeapInuse string // bytes in non-idle span
  47. HeapReleased string // bytes released to the OS
  48. HeapObjects uint64 // total number of allocated objects
  49. // Low-level fixed-size structure allocator statistics.
  50. // Inuse is bytes used now.
  51. // Sys is bytes obtained from system.
  52. StackInuse string // bootstrap stacks
  53. StackSys string
  54. MSpanInuse string // mspan structures
  55. MSpanSys string
  56. MCacheInuse string // mcache structures
  57. MCacheSys string
  58. BuckHashSys string // profiling bucket hash table
  59. GCSys string // GC metadata
  60. OtherSys string // other system allocations
  61. // Garbage collector statistics.
  62. NextGC string // next run in HeapAlloc time (bytes)
  63. LastGC string // last run in absolute time (ns)
  64. PauseTotalNs string
  65. PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
  66. NumGC uint32
  67. }
  68. func updateSystemStatus() {
  69. sysStatus.StartTime = setting.AppStartTime.Format(time.RFC3339)
  70. m := new(runtime.MemStats)
  71. runtime.ReadMemStats(m)
  72. sysStatus.NumGoroutine = runtime.NumGoroutine()
  73. sysStatus.MemAllocated = base.FileSize(int64(m.Alloc))
  74. sysStatus.MemTotal = base.FileSize(int64(m.TotalAlloc))
  75. sysStatus.MemSys = base.FileSize(int64(m.Sys))
  76. sysStatus.Lookups = m.Lookups
  77. sysStatus.MemMallocs = m.Mallocs
  78. sysStatus.MemFrees = m.Frees
  79. sysStatus.HeapAlloc = base.FileSize(int64(m.HeapAlloc))
  80. sysStatus.HeapSys = base.FileSize(int64(m.HeapSys))
  81. sysStatus.HeapIdle = base.FileSize(int64(m.HeapIdle))
  82. sysStatus.HeapInuse = base.FileSize(int64(m.HeapInuse))
  83. sysStatus.HeapReleased = base.FileSize(int64(m.HeapReleased))
  84. sysStatus.HeapObjects = m.HeapObjects
  85. sysStatus.StackInuse = base.FileSize(int64(m.StackInuse))
  86. sysStatus.StackSys = base.FileSize(int64(m.StackSys))
  87. sysStatus.MSpanInuse = base.FileSize(int64(m.MSpanInuse))
  88. sysStatus.MSpanSys = base.FileSize(int64(m.MSpanSys))
  89. sysStatus.MCacheInuse = base.FileSize(int64(m.MCacheInuse))
  90. sysStatus.MCacheSys = base.FileSize(int64(m.MCacheSys))
  91. sysStatus.BuckHashSys = base.FileSize(int64(m.BuckHashSys))
  92. sysStatus.GCSys = base.FileSize(int64(m.GCSys))
  93. sysStatus.OtherSys = base.FileSize(int64(m.OtherSys))
  94. sysStatus.NextGC = base.FileSize(int64(m.NextGC))
  95. sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000)
  96. sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000)
  97. sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000)
  98. sysStatus.NumGC = m.NumGC
  99. }
  100. func prepareDeprecatedWarningsAlert(ctx *context.Context) {
  101. if len(setting.DeprecatedWarnings) > 0 {
  102. content := setting.DeprecatedWarnings[0]
  103. if len(setting.DeprecatedWarnings) > 1 {
  104. content += fmt.Sprintf(" (and %d more)", len(setting.DeprecatedWarnings)-1)
  105. }
  106. ctx.Flash.Error(content, true)
  107. }
  108. }
  109. // Dashboard show admin panel dashboard
  110. func Dashboard(ctx *context.Context) {
  111. ctx.Data["Title"] = ctx.Tr("admin.dashboard")
  112. ctx.Data["PageIsAdminDashboard"] = true
  113. ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate(ctx)
  114. ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion(ctx)
  115. // FIXME: update periodically
  116. updateSystemStatus()
  117. ctx.Data["SysStatus"] = sysStatus
  118. ctx.Data["SSH"] = setting.SSH
  119. prepareDeprecatedWarningsAlert(ctx)
  120. ctx.HTML(http.StatusOK, tplDashboard)
  121. }
  122. // DashboardPost run an admin operation
  123. func DashboardPost(ctx *context.Context) {
  124. form := web.GetForm(ctx).(*forms.AdminDashboardForm)
  125. ctx.Data["Title"] = ctx.Tr("admin.dashboard")
  126. ctx.Data["PageIsAdminDashboard"] = true
  127. updateSystemStatus()
  128. ctx.Data["SysStatus"] = sysStatus
  129. // Run operation.
  130. if form.Op != "" {
  131. switch form.Op {
  132. case "sync_repo_branches":
  133. go func() {
  134. if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), ctx.Doer.ID); err != nil {
  135. log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err)
  136. }
  137. }()
  138. ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_branch.started"))
  139. default:
  140. task := cron.GetTask(form.Op)
  141. if task != nil {
  142. go task.RunWithUser(ctx.Doer, nil)
  143. ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
  144. } else {
  145. ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
  146. }
  147. }
  148. }
  149. if form.From == "monitor" {
  150. ctx.Redirect(setting.AppSubURL + "/admin/monitor/cron")
  151. } else {
  152. ctx.Redirect(setting.AppSubURL + "/admin")
  153. }
  154. }
  155. func CronTasks(ctx *context.Context) {
  156. ctx.Data["Title"] = ctx.Tr("admin.monitor.cron")
  157. ctx.Data["PageIsAdminMonitorCron"] = true
  158. ctx.Data["Entries"] = cron.ListTasks()
  159. ctx.HTML(http.StatusOK, tplCron)
  160. }
  161. func MonitorStats(ctx *context.Context) {
  162. ctx.Data["Title"] = ctx.Tr("admin.monitor.stats")
  163. ctx.Data["PageIsAdminMonitorStats"] = true
  164. bs, err := json.Marshal(activities_model.GetStatistic(ctx).Counter)
  165. if err != nil {
  166. ctx.ServerError("MonitorStats", err)
  167. return
  168. }
  169. statsCounter := map[string]any{}
  170. err = json.Unmarshal(bs, &statsCounter)
  171. if err != nil {
  172. ctx.ServerError("MonitorStats", err)
  173. return
  174. }
  175. statsKeys := make([]string, 0, len(statsCounter))
  176. for k := range statsCounter {
  177. if statsCounter[k] == nil {
  178. continue
  179. }
  180. statsKeys = append(statsKeys, k)
  181. }
  182. sort.Strings(statsKeys)
  183. ctx.Data["StatsKeys"] = statsKeys
  184. ctx.Data["StatsCounter"] = statsCounter
  185. ctx.HTML(http.StatusOK, tplStats)
  186. }