From 03d2147ccee6208dc28eb9712d928ba68801b4db Mon Sep 17 00:00:00 2001 From: Hobo86 Date: Tue, 10 Jul 2018 20:31:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0runtime.MemStats=20Dashboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- router/api/router.go | 2 - router/web/dashboard.go | 106 +++++++++++++++ router/web/router.go | 2 + template/pongo2/base.html | 1 + template/pongo2/web/dashboard.html | 131 ++++++++++++++++++ template/template.go | 21 +++ util/conv/file.go | 77 +++++++++++ util/conv/tool.go | 204 +++++++++++++++++++++++++++++ 8 files changed, 542 insertions(+), 2 deletions(-) create mode 100644 router/web/dashboard.go create mode 100644 template/pongo2/web/dashboard.html create mode 100644 util/conv/file.go create mode 100644 util/conv/tool.go diff --git a/router/api/router.go b/router/api/router.go index 9acce5c..04f3914 100644 --- a/router/api/router.go +++ b/router/api/router.go @@ -53,8 +53,6 @@ func Routers() *echo.Echo { e.Static("/favicon.ico", "./assets/img/favicon.ico") - - // Cache e.Use(cache.Cache()) diff --git a/router/web/dashboard.go b/router/web/dashboard.go new file mode 100644 index 0000000..bc96d1c --- /dev/null +++ b/router/web/dashboard.go @@ -0,0 +1,106 @@ +package web + +import ( + "time" + "runtime" + "fmt" + + "github.com/labstack/echo" + + "github.com/hb-go/echo-web/util/conv" +) + +func DashboardHandler(c echo.Context) error { + updateSystemStatus() + + c.Set("tmpl", "web/dashboard") + c.Set("data", map[string]interface{}{ + "title": "Dashboard", + "SysStatus": sysStatus, + }) + + return nil +} + +var ( + startTime = time.Now() +) + +var sysStatus struct { + Uptime string + NumGoroutine int + + // General statistics. + MemAllocated string // bytes allocated and still in use + MemTotal string // bytes allocated (even if freed) + MemSys string // bytes obtained from system (sum of XxxSys below) + Lookups uint64 // number of pointer lookups + MemMallocs uint64 // number of mallocs + MemFrees uint64 // number of frees + + // Main allocation heap statistics. + HeapAlloc string // bytes allocated and still in use + HeapSys string // bytes obtained from system + HeapIdle string // bytes in idle spans + HeapInuse string // bytes in non-idle span + HeapReleased string // bytes released to the OS + HeapObjects uint64 // total number of allocated objects + + // Low-level fixed-size structure allocator statistics. + // Inuse is bytes used now. + // Sys is bytes obtained from system. + StackInuse string // bootstrap stacks + StackSys string + MSpanInuse string // mspan structures + MSpanSys string + MCacheInuse string // mcache structures + MCacheSys string + BuckHashSys string // profiling bucket hash table + GCSys string // GC metadata + OtherSys string // other system allocations + + // Garbage collector statistics. + NextGC string // next run in HeapAlloc time (bytes) + LastGC string // last run in absolute time (ns) + PauseTotalNs string + PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] + NumGC uint32 +} + +func updateSystemStatus() { + sysStatus.Uptime = conv.TimeSincePro(startTime) + + m := new(runtime.MemStats) + runtime.ReadMemStats(m) + sysStatus.NumGoroutine = runtime.NumGoroutine() + + sysStatus.MemAllocated = conv.FileSize(int64(m.Alloc)) + sysStatus.MemTotal = conv.FileSize(int64(m.TotalAlloc)) + sysStatus.MemSys = conv.FileSize(int64(m.Sys)) + sysStatus.Lookups = m.Lookups + sysStatus.MemMallocs = m.Mallocs + sysStatus.MemFrees = m.Frees + + sysStatus.HeapAlloc = conv.FileSize(int64(m.HeapAlloc)) + sysStatus.HeapSys = conv.FileSize(int64(m.HeapSys)) + sysStatus.HeapIdle = conv.FileSize(int64(m.HeapIdle)) + sysStatus.HeapInuse = conv.FileSize(int64(m.HeapInuse)) + sysStatus.HeapReleased = conv.FileSize(int64(m.HeapReleased)) + sysStatus.HeapObjects = m.HeapObjects + + sysStatus.StackInuse = conv.FileSize(int64(m.StackInuse)) + sysStatus.StackSys = conv.FileSize(int64(m.StackSys)) + sysStatus.MSpanInuse = conv.FileSize(int64(m.MSpanInuse)) + sysStatus.MSpanSys = conv.FileSize(int64(m.MSpanSys)) + sysStatus.MCacheInuse = conv.FileSize(int64(m.MCacheInuse)) + sysStatus.MCacheSys = conv.FileSize(int64(m.MCacheSys)) + sysStatus.BuckHashSys = conv.FileSize(int64(m.BuckHashSys)) + sysStatus.GCSys = conv.FileSize(int64(m.GCSys)) + sysStatus.OtherSys = conv.FileSize(int64(m.OtherSys)) + + sysStatus.NextGC = conv.FileSize(int64(m.NextGC)) + sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) + 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 +} diff --git a/router/web/router.go b/router/web/router.go index 4ba8f58..0a97f2e 100644 --- a/router/web/router.go +++ b/router/web/router.go @@ -93,6 +93,8 @@ func Routers() *echo.Echo { e.POST("/login", handler(LoginPostHandler)) e.POST("/register", handler(RegisterPostHandler)) + e.GET("/dashboard", DashboardHandler) + e.GET("/jwt/tester", handler(JWTTesterHandler)) e.GET("/ws", handler(WsHandler)) diff --git a/template/pongo2/base.html b/template/pongo2/base.html index 8574abf..b838ecc 100644 --- a/template/pongo2/base.html +++ b/template/pongo2/base.html @@ -34,6 +34,7 @@
  • JWT
  • Socket
  • +
  • Dashboard
  • Jaeger
  • GitHub
  • diff --git a/template/pongo2/web/dashboard.html b/template/pongo2/web/dashboard.html new file mode 100644 index 0000000..5f70240 --- /dev/null +++ b/template/pongo2/web/dashboard.html @@ -0,0 +1,131 @@ +{% extends "../base.html" %} + +{% block head_title %} +{{title}} +{% endblock %} + + +{% block head_scripts %} + +{% endblock %} + +{% block content %} +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    服务运行时间{{SysStatus.Uptime}}
    当前 Goroutines 数量{{SysStatus.NumGoroutine}}
    当前内存使用量{{SysStatus.MemAllocated}}
    所有被分配的内存{{SysStatus.MemTotal}}
    内存占用量{{SysStatus.MemSys}}
    指针查找次数{{SysStatus.Lookups}}
    内存分配次数{{SysStatus.MemMallocs}}
    内存释放次数{{SysStatus.MemFrees}}
    当前 Heap 内存使用量{{SysStatus.HeapAlloc}}
    Heap 内存占用量{{SysStatus.HeapSys}}
    Heap 内存空闲量{{SysStatus.HeapIdle}}
    正在使用的 Heap 内存{{SysStatus.HeapInuse}}
    被释放的 Heap 内存{{SysStatus.HeapReleased}}
    Heap 对象数量{{SysStatus.HeapObjects}}
    启动 Stack 使用量{{SysStatus.StackInuse}}
    被分配的 Stack 内存{{SysStatus.StackSys}}
    MSpan 结构内存使用量{{SysStatus.MSpanInuse}}
    被分配的 MSpan 结构内存{{SysStatus.HeapSys}}
    MCache 结构内存使用量{{SysStatus.MCacheInuse}}
    被分配的 MCache 结构内存{{SysStatus.MCacheSys}}
    被分配的剖析哈希表内存{{SysStatus.BuckHashSys}}
    被分配的 GC 元数据内存{{SysStatus.GCSys}}
    其它被分配的系统内存{{SysStatus.OtherSys}}
    下次 GC 内存回收量{{SysStatus.NextGC}}
    距离上次 GC 时间{{SysStatus.LastGC}}
    GC 执行时间总量{{SysStatus.PauseTotalNs}}
    GC 暂停时间总量{{SysStatus.PauseNs}}
    上次 GC 暂停时间{{SysStatus.NumGC}}
    +
    +{% endblock %} \ No newline at end of file diff --git a/template/template.go b/template/template.go index a25dd90..48e2268 100644 --- a/template/template.go +++ b/template/template.go @@ -12,6 +12,7 @@ // template/layout.tmpl // template/pongo2/base.html // template/pongo2/web/about.html +// template/pongo2/web/dashboard.html // template/pongo2/web/home.html // template/pongo2/web/index.html // template/pongo2/web/jwt_tester.html @@ -261,6 +262,24 @@ func templatePongo2WebAboutHtml() (*asset, error) { return a, err } +// templatePongo2WebDashboardHtml reads file data from disk. It returns an error on failure. +func templatePongo2WebDashboardHtml() (*asset, error) { + path := "/Users/Steven/Develop/Go/project/src/github.com/hb-go/echo-web/template/pongo2/web/dashboard.html" + name := "template/pongo2/web/dashboard.html" + bytes, err := bindataRead(path, name) + if err != nil { + return nil, err + } + + fi, err := os.Stat(path) + if err != nil { + err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err) + } + + a := &asset{bytes: bytes, info: fi} + return a, err +} + // templatePongo2WebHomeHtml reads file data from disk. It returns an error on failure. func templatePongo2WebHomeHtml() (*asset, error) { path := "/Users/Steven/Develop/Go/project/src/github.com/hb-go/echo-web/template/pongo2/web/home.html" @@ -451,6 +470,7 @@ var _bindata = map[string]func() (*asset, error){ "template/layout.tmpl": templateLayoutTmpl, "template/pongo2/base.html": templatePongo2BaseHtml, "template/pongo2/web/about.html": templatePongo2WebAboutHtml, + "template/pongo2/web/dashboard.html": templatePongo2WebDashboardHtml, "template/pongo2/web/home.html": templatePongo2WebHomeHtml, "template/pongo2/web/index.html": templatePongo2WebIndexHtml, "template/pongo2/web/jwt_tester.html": templatePongo2WebJwt_testerHtml, @@ -521,6 +541,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "base.html": &bintree{templatePongo2BaseHtml, map[string]*bintree{}}, "web": &bintree{nil, map[string]*bintree{ "about.html": &bintree{templatePongo2WebAboutHtml, map[string]*bintree{}}, + "dashboard.html": &bintree{templatePongo2WebDashboardHtml, map[string]*bintree{}}, "home.html": &bintree{templatePongo2WebHomeHtml, map[string]*bintree{}}, "index.html": &bintree{templatePongo2WebIndexHtml, map[string]*bintree{}}, "jwt_tester.html": &bintree{templatePongo2WebJwt_testerHtml, map[string]*bintree{}}, diff --git a/util/conv/file.go b/util/conv/file.go new file mode 100644 index 0000000..7f207f2 --- /dev/null +++ b/util/conv/file.go @@ -0,0 +1,77 @@ +// Copyright 2017 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package conv + +import ( + "fmt" + "math" + "net/http" + "strings" +) + +// IsTextFile returns true if file content format is plain text or empty. +func IsTextFile(data []byte) bool { + if len(data) == 0 { + return true + } + return strings.Contains(http.DetectContentType(data), "text/") +} + +func IsImageFile(data []byte) bool { + return strings.Contains(http.DetectContentType(data), "image/") +} + +func IsPDFFile(data []byte) bool { + return strings.Contains(http.DetectContentType(data), "application/pdf") +} + +func IsVideoFile(data []byte) bool { + return strings.Contains(http.DetectContentType(data), "video/") +} + +const ( + Byte = 1 + KByte = Byte * 1024 + MByte = KByte * 1024 + GByte = MByte * 1024 + TByte = GByte * 1024 + PByte = TByte * 1024 + EByte = PByte * 1024 +) + +var bytesSizeTable = map[string]uint64{ + "b": Byte, + "kb": KByte, + "mb": MByte, + "gb": GByte, + "tb": TByte, + "pb": PByte, + "eb": EByte, +} + +func logn(n, b float64) float64 { + return math.Log(n) / math.Log(b) +} + +func humanateBytes(s uint64, base float64, sizes []string) string { + if s < 10 { + return fmt.Sprintf("%d B", s) + } + e := math.Floor(logn(float64(s), base)) + suffix := sizes[int(e)] + val := float64(s) / math.Pow(base, math.Floor(e)) + f := "%.0f" + if val < 10 { + f = "%.1f" + } + + return fmt.Sprintf(f+" %s", val, suffix) +} + +// FileSize calculates the file size and generate user-friendly string. +func FileSize(s int64) string { + sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(uint64(s), 1024, sizes) +} \ No newline at end of file diff --git a/util/conv/tool.go b/util/conv/tool.go new file mode 100644 index 0000000..bd945e5 --- /dev/null +++ b/util/conv/tool.go @@ -0,0 +1,204 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package conv + +import ( + "fmt" + "strings" + "time" + "unicode" + "strconv" +) + +// Seconds-based time units +const ( + Minute = 60 + Hour = 60 * Minute + Day = 24 * Hour + Week = 7 * Day + Month = 30 * Day + Year = 12 * Month +) + +func computeTimeDiff(diff int64) (int64, string) { + diffStr := "" + switch { + case diff <= 0: + diff = 0 + diffStr = "now" + case diff < 2: + diff = 0 + diffStr = "1 second" + case diff < 1*Minute: + diffStr = fmt.Sprintf("%d seconds", diff) + diff = 0 + + case diff < 2*Minute: + diff -= 1 * Minute + diffStr = "1 minute" + case diff < 1*Hour: + diffStr = fmt.Sprintf("%d minutes", diff/Minute) + diff -= diff / Minute * Minute + + case diff < 2*Hour: + diff -= 1 * Hour + diffStr = "1 hour" + case diff < 1*Day: + diffStr = fmt.Sprintf("%d hours", diff/Hour) + diff -= diff / Hour * Hour + + case diff < 2*Day: + diff -= 1 * Day + diffStr = "1 day" + case diff < 1*Week: + diffStr = fmt.Sprintf("%d days", diff/Day) + diff -= diff / Day * Day + + case diff < 2*Week: + diff -= 1 * Week + diffStr = "1 week" + case diff < 1*Month: + diffStr = fmt.Sprintf("%d weeks", diff/Week) + diff -= diff / Week * Week + + case diff < 2*Month: + diff -= 1 * Month + diffStr = "1 month" + case diff < 1*Year: + diffStr = fmt.Sprintf("%d months", diff/Month) + diff -= diff / Month * Month + + case diff < 2*Year: + diff -= 1 * Year + diffStr = "1 year" + default: + diffStr = fmt.Sprintf("%d years", diff/Year) + diff = 0 + } + return diff, diffStr +} + +// TimeSincePro calculates the time interval and generate full user-friendly string. +func TimeSincePro(then time.Time) string { + now := time.Now() + diff := now.Unix() - then.Unix() + + if then.After(now) { + return "future" + } + + var timeStr, diffStr string + for { + if diff == 0 { + break + } + + diff, diffStr = computeTimeDiff(diff) + timeStr += ", " + diffStr + } + return strings.TrimPrefix(timeStr, ", ") +} + +// Subtract deals with subtraction of all types of number. +func Subtract(left interface{}, right interface{}) interface{} { + var rleft, rright int64 + var fleft, fright float64 + var isInt bool = true + switch left.(type) { + case int: + rleft = int64(left.(int)) + case int8: + rleft = int64(left.(int8)) + case int16: + rleft = int64(left.(int16)) + case int32: + rleft = int64(left.(int32)) + case int64: + rleft = left.(int64) + case float32: + fleft = float64(left.(float32)) + isInt = false + case float64: + fleft = left.(float64) + isInt = false + } + + switch right.(type) { + case int: + rright = int64(right.(int)) + case int8: + rright = int64(right.(int8)) + case int16: + rright = int64(right.(int16)) + case int32: + rright = int64(right.(int32)) + case int64: + rright = right.(int64) + case float32: + fright = float64(left.(float32)) + isInt = false + case float64: + fleft = left.(float64) + isInt = false + } + + if isInt { + return rleft - rright + } else { + return fleft + float64(rleft) - (fright + float64(rright)) + } +} + +// EllipsisString returns a truncated short string, +// it appends '...' in the end of the length of string is too large. +func EllipsisString(str string, length int) string { + if len(str) < length { + return str + } + return str[:length-3] + "..." +} + +// TruncateString returns a truncated string with given limit, +// it returns input string if length is not reached limit. +func TruncateString(str string, limit int) string { + if len(str) < limit { + return str + } + return str[:limit] +} + +// StringsToInt64s converts a slice of string to a slice of int64. +func StringsToInt64s(strs []string) []int64 { + ints := make([]int64, len(strs)) + for i := range strs { + v, _ := strconv.ParseInt(strs[i], 10, 64) + ints[i] =v + } + return ints +} + +// Int64sToStrings converts a slice of int64 to a slice of string. +func Int64sToStrings(ints []int64) []string { + strs := make([]string, len(ints)) + for i := range ints { + strs[i]= strconv.FormatInt(ints[i],10) + } + return strs +} + +// Int64sToMap converts a slice of int64 to a int64 map. +func Int64sToMap(ints []int64) map[int64]bool { + m := make(map[int64]bool) + for _, i := range ints { + m[i] = true + } + return m +} + +// IsLetter reports whether the rune is a letter (category L). +// https://github.com/golang/go/blob/master/src/go/scanner/scanner.go#L257 +func IsLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) +}