From c70f65e83bc1876fb368fd117d342573ff18a9e8 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 16 Feb 2024 04:52:25 +0200 Subject: [PATCH] Auto-update the system status in admin dashboard (#29163) - Refactor the system status list into its own template - Change the backend to return only the system status if htmx initiated the request - `hx-get="{{$.Link}}/system_status`: reuse the backend handler - `hx-swap="innerHTML"`: replace the `
`'s innerHTML (essentially the new template) - `hx-trigger="every 5s"`: call every 5 seconds - `hx-indicator=".divider"`: the `is-loading` class shouldn't be added to the div during the request, so set it on an element it has no effect on - Render "Since Last GC Time" with ``, so we send a timestamp # Auto-update in action GIF ![action](https://github.com/go-gitea/gitea/assets/20454870/c6e1f220-f0fb-4460-ac3b-59f315e30e29) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind --- package-lock.json | 6 +++ package.json | 1 + routers/web/admin/admin.go | 26 +++++++----- routers/web/web.go | 1 + templates/admin/dashboard.tmpl | 66 ++---------------------------- templates/admin/system_status.tmpl | 62 ++++++++++++++++++++++++++++ templates/base/head.tmpl | 2 +- web_src/js/htmx.js | 3 ++ webpack.config.js | 4 ++ 9 files changed, 97 insertions(+), 74 deletions(-) create mode 100644 templates/admin/system_status.tmpl diff --git a/package-lock.json b/package-lock.json index 48da8124e..13f03b8d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "escape-goat": "4.0.0", "fast-glob": "3.3.2", "htmx.org": "1.9.10", + "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", @@ -6174,6 +6175,11 @@ "postcss": "^8.1.0" } }, + "node_modules/idiomorph": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/idiomorph/-/idiomorph-0.3.0.tgz", + "integrity": "sha512-UhV1Ey5xCxIwR9B+OgIjQa+1Jx99XQ1vQHUsKBU1RpQzCx1u+b+N6SOXgf5mEJDqemUI/ffccu6+71l2mJUsRA==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", diff --git a/package.json b/package.json index ac7974171..3d753a567 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "escape-goat": "4.0.0", "fast-glob": "3.3.2", "htmx.org": "1.9.10", + "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index d31cb1cd2..9fbd429f7 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -28,13 +28,14 @@ import ( ) const ( - tplDashboard base.TplName = "admin/dashboard" - tplSelfCheck base.TplName = "admin/self_check" - tplCron base.TplName = "admin/cron" - tplQueue base.TplName = "admin/queue" - tplStacktrace base.TplName = "admin/stacktrace" - tplQueueManage base.TplName = "admin/queue_manage" - tplStats base.TplName = "admin/stats" + tplDashboard base.TplName = "admin/dashboard" + tplSystemStatus base.TplName = "admin/system_status" + tplSelfCheck base.TplName = "admin/self_check" + tplCron base.TplName = "admin/cron" + tplQueue base.TplName = "admin/queue" + tplStacktrace base.TplName = "admin/stacktrace" + tplQueueManage base.TplName = "admin/queue_manage" + tplStats base.TplName = "admin/stats" ) var sysStatus struct { @@ -72,7 +73,7 @@ var sysStatus struct { // Garbage collector statistics. NextGC string // next run in HeapAlloc time (bytes) - LastGC string // last run in absolute time (ns) + LastGCTime string // last run time PauseTotalNs string PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] NumGC uint32 @@ -110,7 +111,7 @@ func updateSystemStatus() { sysStatus.OtherSys = base.FileSize(int64(m.OtherSys)) sysStatus.NextGC = base.FileSize(int64(m.NextGC)) - sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) + sysStatus.LastGCTime = time.Unix(0, int64(m.LastGC)).Format(time.RFC3339) sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000) sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000) sysStatus.NumGC = m.NumGC @@ -132,7 +133,6 @@ func Dashboard(ctx *context.Context) { ctx.Data["PageIsAdminDashboard"] = true ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate(ctx) ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion(ctx) - // FIXME: update periodically updateSystemStatus() ctx.Data["SysStatus"] = sysStatus ctx.Data["SSH"] = setting.SSH @@ -140,6 +140,12 @@ func Dashboard(ctx *context.Context) { ctx.HTML(http.StatusOK, tplDashboard) } +func SystemStatus(ctx *context.Context) { + updateSystemStatus() + ctx.Data["SysStatus"] = sysStatus + ctx.HTML(http.StatusOK, tplSystemStatus) +} + // DashboardPost run an admin operation func DashboardPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AdminDashboardForm) diff --git a/routers/web/web.go b/routers/web/web.go index a6288caaf..0528b2032 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -677,6 +677,7 @@ func registerRoutes(m *web.Route) { // ***** START: Admin ***** m.Group("/admin", func() { m.Get("", admin.Dashboard) + m.Get("/system_status", admin.SystemStatus) m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost) m.Get("/self_check", admin.SelfCheck) diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index f43b4c538..8088315f1 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -75,69 +75,9 @@

{{ctx.Locale.Tr "admin.dashboard.system_status"}}

-
-
-
{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}
-
{{.SysStatus.StartTime}}
-
{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}
-
{{.SysStatus.NumGoroutine}}
-
-
{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}
-
{{.SysStatus.MemAllocated}}
-
{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}
-
{{.SysStatus.MemTotal}}
-
{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}
-
{{.SysStatus.MemSys}}
-
{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}
-
{{.SysStatus.Lookups}}
-
{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}
-
{{.SysStatus.MemMallocs}}
-
{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}
-
{{.SysStatus.MemFrees}}
-
-
{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}
-
{{.SysStatus.HeapAlloc}}
-
{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}
-
{{.SysStatus.HeapSys}}
-
{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}
-
{{.SysStatus.HeapIdle}}
-
{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}
-
{{.SysStatus.HeapInuse}}
-
{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}
-
{{.SysStatus.HeapReleased}}
-
{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}
-
{{.SysStatus.HeapObjects}}
-
-
{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}
-
{{.SysStatus.StackInuse}}
-
{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}
-
{{.SysStatus.StackSys}}
-
{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}
-
{{.SysStatus.MSpanInuse}}
-
{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}
-
{{.SysStatus.MSpanSys}}
-
{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}
-
{{.SysStatus.MCacheInuse}}
-
{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}
-
{{.SysStatus.MCacheSys}}
-
{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}
-
{{.SysStatus.BuckHashSys}}
-
{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}
-
{{.SysStatus.GCSys}}
-
{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}
-
{{.SysStatus.OtherSys}}
-
-
{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}
-
{{.SysStatus.NextGC}}
-
{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}
-
{{.SysStatus.LastGC}}
-
{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}
-
{{.SysStatus.PauseTotalNs}}
-
{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}
-
{{.SysStatus.PauseNs}}
-
{{ctx.Locale.Tr "admin.dashboard.gc_times"}}
-
{{.SysStatus.NumGC}}
-
+ {{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}} +
+ {{template "admin/system_status" .}}
{{template "admin/layout_footer" .}} diff --git a/templates/admin/system_status.tmpl b/templates/admin/system_status.tmpl new file mode 100644 index 000000000..7b5c9be6c --- /dev/null +++ b/templates/admin/system_status.tmpl @@ -0,0 +1,62 @@ +
+
{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}
+
{{.SysStatus.StartTime}}
+
{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}
+
{{.SysStatus.NumGoroutine}}
+
+
{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}
+
{{.SysStatus.MemAllocated}}
+
{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}
+
{{.SysStatus.MemTotal}}
+
{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}
+
{{.SysStatus.MemSys}}
+
{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}
+
{{.SysStatus.Lookups}}
+
{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}
+
{{.SysStatus.MemMallocs}}
+
{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}
+
{{.SysStatus.MemFrees}}
+
+
{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}
+
{{.SysStatus.HeapAlloc}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}
+
{{.SysStatus.HeapSys}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}
+
{{.SysStatus.HeapIdle}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}
+
{{.SysStatus.HeapInuse}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}
+
{{.SysStatus.HeapReleased}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}
+
{{.SysStatus.HeapObjects}}
+
+
{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}
+
{{.SysStatus.StackInuse}}
+
{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}
+
{{.SysStatus.StackSys}}
+
{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}
+
{{.SysStatus.MSpanInuse}}
+
{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}
+
{{.SysStatus.MSpanSys}}
+
{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}
+
{{.SysStatus.MCacheInuse}}
+
{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}
+
{{.SysStatus.MCacheSys}}
+
{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}
+
{{.SysStatus.BuckHashSys}}
+
{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}
+
{{.SysStatus.GCSys}}
+
{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}
+
{{.SysStatus.OtherSys}}
+
+
{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}
+
{{.SysStatus.NextGC}}
+
{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}
+
{{.SysStatus.LastGCTime}}
+
{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}
+
{{.SysStatus.PauseTotalNs}}
+
{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}
+
{{.SysStatus.PauseNs}}
+
{{ctx.Locale.Tr "admin.dashboard.gc_times"}}
+
{{.SysStatus.NumGC}}
+
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index b9c050fdd..e910bb0cd 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -29,7 +29,7 @@ {{template "base/head_style" .}} {{template "custom/header" .}} - + {{ctx.DataRaceCheck $.Context}} {{template "custom/body_outer_pre" .}} diff --git a/web_src/js/htmx.js b/web_src/js/htmx.js index 92400d1cb..5ca301830 100644 --- a/web_src/js/htmx.js +++ b/web_src/js/htmx.js @@ -1,6 +1,9 @@ import * as htmx from 'htmx.org'; import {showErrorToast} from './modules/toast.js'; +// https://github.com/bigskysoftware/idiomorph#htmx +import 'idiomorph/dist/idiomorph-ext.js'; + // https://htmx.org/reference/#config htmx.config.requestClass = 'is-loading'; htmx.config.scrollIntoViewOnBoost = false; diff --git a/webpack.config.js b/webpack.config.js index 8b3b8477c..82d76d9e8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -169,6 +169,9 @@ export default { ], }, plugins: [ + new webpack.ProvidePlugin({ // for htmx extensions + htmx: 'htmx.org', + }), new DefinePlugin({ __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API __VUE_PROD_DEVTOOLS__: false, // do not enable devtools support in production @@ -207,6 +210,7 @@ export default { override: { 'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33 'htmx.org@1.9.10': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" + 'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" }, emitError: true, allow: '(Apache-2.0 OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)',