1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-25 23:01:28 +00:00

Added the UI for Admin monitor page

This commit is contained in:
Lei Cao 2014-08-14 17:35:23 +08:00
parent 14cd9e51ac
commit d314d12c77
3 changed files with 592 additions and 123 deletions

375
admin.go
View File

@ -1,18 +1,15 @@
// Beego (http://beego.me/) // Beego (http://beego.me/)
//
// @description beego is an open-source, high-performance web framework for the Go programming language. // @description beego is an open-source, high-performance web framework for the Go programming language.
//
// @link http://github.com/astaxie/beego for the canonical source repository // @link http://github.com/astaxie/beego for the canonical source repository
//
// @license http://github.com/astaxie/beego/blob/master/LICENSE // @license http://github.com/astaxie/beego/blob/master/LICENSE
//
// @authors astaxie // @authors astaxie
package beego package beego
import ( import (
"bytes"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "text/template"
"time" "time"
"github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/toolbox"
@ -49,7 +46,6 @@ func init() {
beeAdminApp.Route("/prof", profIndex) beeAdminApp.Route("/prof", profIndex)
beeAdminApp.Route("/healthcheck", healthcheck) beeAdminApp.Route("/healthcheck", healthcheck)
beeAdminApp.Route("/task", taskStatus) beeAdminApp.Route("/task", taskStatus)
beeAdminApp.Route("/runtask", runTask)
beeAdminApp.Route("/listconf", listConf) beeAdminApp.Route("/listconf", listConf)
FilterMonitorFunc = func(string, string, time.Duration) bool { return true } FilterMonitorFunc = func(string, string, time.Duration) bool { return true }
} }
@ -57,22 +53,22 @@ func init() {
// AdminIndex is the default http.Handler for admin module. // AdminIndex is the default http.Handler for admin module.
// it matches url pattern "/". // it matches url pattern "/".
func adminIndex(rw http.ResponseWriter, r *http.Request) { func adminIndex(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>")) tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
rw.Write([]byte("Welcome to Admin Dashboard<br>\n")) tmpl = template.Must(tmpl.Parse(indexTpl))
rw.Write([]byte("There are servral functions:<br>\n")) data := make(map[interface{}]interface{})
rw.Write([]byte("1. Record all request and request time, <a href='/qps'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/qps</a><br>\n")) tmpl.Execute(rw, data)
rw.Write([]byte("2. Get runtime profiling data by the pprof, <a href='/prof'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/prof</a><br>\n"))
rw.Write([]byte("3. Get healthcheck result from <a href='/healthcheck'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/healthcheck</a><br>\n"))
rw.Write([]byte("4. Get current task infomation from task <a href='/task'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/task</a><br> \n"))
rw.Write([]byte("5. To run a task passed a param <a href='/runtask'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/runtask</a><br>\n"))
rw.Write([]byte("6. Get all confige & router infomation <a href='/listconf'>http://localhost:" + strconv.Itoa(AdminHttpPort) + "/listconf</a><br>\n"))
rw.Write([]byte("</body></html>"))
} }
// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter. // QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.
// it's registered with url pattern "/qbs" in admin module. // it's registered with url pattern "/qbs" in admin module.
func qpsIndex(rw http.ResponseWriter, r *http.Request) { func qpsIndex(rw http.ResponseWriter, r *http.Request) {
toolbox.StatisticsMap.GetMap(rw) tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(qpsTpl))
data := make(map[interface{}]interface{})
data["Content"] = toolbox.StatisticsMap.GetMap()
tmpl.Execute(rw, data)
} }
// ListConf is the http.Handler of displaying all beego configuration values as key/value pair. // ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
@ -81,112 +77,217 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
command := r.Form.Get("command") command := r.Form.Get("command")
if command != "" { if command != "" {
data := make(map[interface{}]interface{})
switch command { switch command {
case "conf": case "conf":
fmt.Fprintln(rw, "list all beego's conf:") m := make(map[string]interface{})
fmt.Fprintln(rw, "AppName:", AppName)
fmt.Fprintln(rw, "AppPath:", AppPath) m["AppName"] = AppName
fmt.Fprintln(rw, "AppConfigPath:", AppConfigPath) m["AppPath"] = AppPath
fmt.Fprintln(rw, "StaticDir:", StaticDir) m["AppConfigPath"] = AppConfigPath
fmt.Fprintln(rw, "StaticExtensionsToGzip:", StaticExtensionsToGzip) m["StaticDir"] = StaticDir
fmt.Fprintln(rw, "HttpAddr:", HttpAddr) m["StaticExtensionsToGzip"] = StaticExtensionsToGzip
fmt.Fprintln(rw, "HttpPort:", HttpPort) m["HttpAddr"] = HttpAddr
fmt.Fprintln(rw, "HttpTLS:", EnableHttpTLS) m["HttpPort"] = HttpPort
fmt.Fprintln(rw, "HttpCertFile:", HttpCertFile) m["HttpTLS"] = EnableHttpTLS
fmt.Fprintln(rw, "HttpKeyFile:", HttpKeyFile) m["HttpCertFile"] = HttpCertFile
fmt.Fprintln(rw, "RecoverPanic:", RecoverPanic) m["HttpKeyFile"] = HttpKeyFile
fmt.Fprintln(rw, "AutoRender:", AutoRender) m["RecoverPanic"] = RecoverPanic
fmt.Fprintln(rw, "ViewsPath:", ViewsPath) m["AutoRender"] = AutoRender
fmt.Fprintln(rw, "RunMode:", RunMode) m["ViewsPath"] = ViewsPath
fmt.Fprintln(rw, "SessionOn:", SessionOn) m["RunMode"] = RunMode
fmt.Fprintln(rw, "SessionProvider:", SessionProvider) m["SessionOn"] = SessionOn
fmt.Fprintln(rw, "SessionName:", SessionName) m["SessionProvider"] = SessionProvider
fmt.Fprintln(rw, "SessionGCMaxLifetime:", SessionGCMaxLifetime) m["SessionName"] = SessionName
fmt.Fprintln(rw, "SessionSavePath:", SessionSavePath) m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
fmt.Fprintln(rw, "SessionHashFunc:", SessionHashFunc) m["SessionSavePath"] = SessionSavePath
fmt.Fprintln(rw, "SessionHashKey:", SessionHashKey) m["SessionHashFunc"] = SessionHashFunc
fmt.Fprintln(rw, "SessionCookieLifeTime:", SessionCookieLifeTime) m["SessionHashKey"] = SessionHashKey
fmt.Fprintln(rw, "UseFcgi:", UseFcgi) m["SessionCookieLifeTime"] = SessionCookieLifeTime
fmt.Fprintln(rw, "MaxMemory:", MaxMemory) m["UseFcgi"] = UseFcgi
fmt.Fprintln(rw, "EnableGzip:", EnableGzip) m["MaxMemory"] = MaxMemory
fmt.Fprintln(rw, "DirectoryIndex:", DirectoryIndex) m["EnableGzip"] = EnableGzip
fmt.Fprintln(rw, "HttpServerTimeOut:", HttpServerTimeOut) m["DirectoryIndex"] = DirectoryIndex
fmt.Fprintln(rw, "ErrorsShow:", ErrorsShow) m["HttpServerTimeOut"] = HttpServerTimeOut
fmt.Fprintln(rw, "XSRFKEY:", XSRFKEY) m["ErrorsShow"] = ErrorsShow
fmt.Fprintln(rw, "EnableXSRF:", EnableXSRF) m["XSRFKEY"] = XSRFKEY
fmt.Fprintln(rw, "XSRFExpire:", XSRFExpire) m["EnableXSRF"] = EnableXSRF
fmt.Fprintln(rw, "CopyRequestBody:", CopyRequestBody) m["XSRFExpire"] = XSRFExpire
fmt.Fprintln(rw, "TemplateLeft:", TemplateLeft) m["CopyRequestBody"] = CopyRequestBody
fmt.Fprintln(rw, "TemplateRight:", TemplateRight) m["TemplateLeft"] = TemplateLeft
fmt.Fprintln(rw, "BeegoServerName:", BeegoServerName) m["TemplateRight"] = TemplateRight
fmt.Fprintln(rw, "EnableAdmin:", EnableAdmin) m["BeegoServerName"] = BeegoServerName
fmt.Fprintln(rw, "AdminHttpAddr:", AdminHttpAddr) m["EnableAdmin"] = EnableAdmin
fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort) m["AdminHttpAddr"] = AdminHttpAddr
m["AdminHttpPort"] = AdminHttpPort
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(configTpl))
data["Content"] = m
tmpl.Execute(rw, data)
case "router": case "router":
fmt.Fprintln(rw, "Print all router infomation:") resultList := new([][]string)
for method, t := range BeeApp.Handlers.routers {
fmt.Fprintln(rw) var result = []string{
fmt.Fprintln(rw) fmt.Sprintf("header"),
fmt.Fprintln(rw, " Method:", method) fmt.Sprintf("Router Pattern"),
printTree(rw, t) fmt.Sprintf("Methods"),
fmt.Sprintf("Controller"),
} }
// @todo print routers *resultList = append(*resultList, result)
for method, t := range BeeApp.Handlers.routers {
var result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Method: %s", method),
fmt.Sprintf(""),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
printTree(resultList, t)
}
data["Content"] = resultList
data["Title"] = "Routers"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(routerAndFilterTpl))
tmpl.Execute(rw, data)
case "filter": case "filter":
fmt.Fprintln(rw, "Print all filter infomation:") resultList := new([][]string)
var result = []string{
fmt.Sprintf("header"),
fmt.Sprintf("Router Pattern"),
fmt.Sprintf("Filter Function"),
}
*resultList = append(*resultList, result)
if BeeApp.Handlers.enableFilter { if BeeApp.Handlers.enableFilter {
fmt.Fprintln(rw, "BeforeRouter:") var result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Before Router"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok { if bf, ok := BeeApp.Handlers.filters[BeforeRouter]; ok {
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
} }
fmt.Fprintln(rw, "BeforeExec:") result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Before Exec"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok { if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
} }
fmt.Fprintln(rw, "AfterExec:") result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("AfterExec Exec"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok { if bf, ok := BeeApp.Handlers.filters[AfterExec]; ok {
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
} }
fmt.Fprintln(rw, "FinishRouter:") result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("Finish Router"),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok { if bf, ok := BeeApp.Handlers.filters[FinishRouter]; ok {
for _, f := range bf { for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", f.pattern),
fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
} }
} }
} }
data["Content"] = resultList
data["Title"] = "Filters"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(routerAndFilterTpl))
tmpl.Execute(rw, data)
default: default:
rw.Write([]byte("command not support")) rw.Write([]byte("command not support"))
} }
} else { } else {
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>"))
rw.Write([]byte("ListConf support this command:<br>\n"))
rw.Write([]byte("1. <a href='?command=conf'>command=conf</a><br>\n"))
rw.Write([]byte("2. <a href='?command=router'>command=router</a><br>\n"))
rw.Write([]byte("3. <a href='?command=filter'>command=filter</a><br>\n"))
rw.Write([]byte("</body></html>"))
} }
} }
func printTree(rw http.ResponseWriter, t *Tree) { func printTree(resultList *[][]string, t *Tree) {
for _, tr := range t.fixrouters { for _, tr := range t.fixrouters {
printTree(rw, tr) printTree(resultList, tr)
} }
if t.wildcard != nil { if t.wildcard != nil {
printTree(rw, t.wildcard) printTree(resultList, t.wildcard)
} }
for _, l := range t.leaves { for _, l := range t.leaves {
if v, ok := l.runObject.(*controllerInfo); ok { if v, ok := l.runObject.(*controllerInfo); ok {
if v.routerType == routerTypeBeego { if v.routerType == routerTypeBeego {
fmt.Fprintln(rw, v.pattern, v.methods, v.controllerType.Name()) var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf("%s", v.controllerType),
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeRESTFul { } else if v.routerType == routerTypeRESTFul {
fmt.Fprintln(rw, v.pattern, v.methods) var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf("%s", v.methods),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeHandler { } else if v.routerType == routerTypeHandler {
fmt.Fprintln(rw, v.pattern, "handler") var result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", v.pattern),
fmt.Sprintf(""),
fmt.Sprintf(""),
}
*resultList = append(*resultList, result)
} }
} }
} }
@ -197,58 +298,106 @@ func printTree(rw http.ResponseWriter, t *Tree) {
func profIndex(rw http.ResponseWriter, r *http.Request) { func profIndex(rw http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
command := r.Form.Get("command") command := r.Form.Get("command")
data := make(map[interface{}]interface{})
var result bytes.Buffer
if command != "" { if command != "" {
toolbox.ProcessInput(command, rw) toolbox.ProcessInput(command, &result)
data["Content"] = result.String()
data["Title"] = command
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(profillingTpl))
tmpl.Execute(rw, data)
} else { } else {
rw.Write([]byte("<html><head><title>beego admin dashboard</title></head><body>"))
rw.Write([]byte("request url like '/prof?command=lookup goroutine'<br>\n"))
rw.Write([]byte("the command have below types:<br>\n"))
rw.Write([]byte("1. <a href='?command=lookup goroutine'>lookup goroutine</a><br>\n"))
rw.Write([]byte("2. <a href='?command=lookup heap'>lookup heap</a><br>\n"))
rw.Write([]byte("3. <a href='?command=lookup threadcreate'>lookup threadcreate</a><br>\n"))
rw.Write([]byte("4. <a href='?command=lookup block'>lookup block</a><br>\n"))
rw.Write([]byte("5. <a href='?command=start cpuprof'>start cpuprof</a><br>\n"))
rw.Write([]byte("6. <a href='?command=stop cpuprof'>stop cpuprof</a><br>\n"))
rw.Write([]byte("7. <a href='?command=get memprof'>get memprof</a><br>\n"))
rw.Write([]byte("8. <a href='?command=gc summary'>gc summary</a><br>\n"))
rw.Write([]byte("</body></html>"))
} }
} }
// Healthcheck is a http.Handler calling health checking and showing the result. // Healthcheck is a http.Handler calling health checking and showing the result.
// it's in "/healthcheck" pattern in admin module. // it's in "/healthcheck" pattern in admin module.
func healthcheck(rw http.ResponseWriter, req *http.Request) { func healthcheck(rw http.ResponseWriter, req *http.Request) {
data := make(map[interface{}]interface{})
resultList := new([][]string)
var result = []string{
fmt.Sprintf("header"),
fmt.Sprintf("Name"),
fmt.Sprintf("Status"),
}
*resultList = append(*resultList, result)
for name, h := range toolbox.AdminCheckList { for name, h := range toolbox.AdminCheckList {
if err := h.Check(); err != nil { if err := h.Check(); err != nil {
fmt.Fprintf(rw, "%s : %s\n", name, err.Error()) result = []string{
fmt.Sprintf("error"),
fmt.Sprintf("%s", name),
fmt.Sprintf("%s", err.Error()),
}
} else { } else {
fmt.Fprintf(rw, "%s : ok\n", name) result = []string{
fmt.Sprintf("success"),
fmt.Sprintf("%s", name),
fmt.Sprintf("OK"),
}
} }
*resultList = append(*resultList, result)
} }
data["Content"] = resultList
data["Title"] = "Health Check"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(healthCheckTpl))
tmpl.Execute(rw, data)
} }
// TaskStatus is a http.Handler with running task status (task name, status and the last execution). // TaskStatus is a http.Handler with running task status (task name, status and the last execution).
// it's in "/task" pattern in admin module. // it's in "/task" pattern in admin module.
func taskStatus(rw http.ResponseWriter, req *http.Request) { func taskStatus(rw http.ResponseWriter, req *http.Request) {
for tname, tk := range toolbox.AdminTaskList { data := make(map[interface{}]interface{})
fmt.Fprintf(rw, "%s:%s:%s", tname, tk.GetStatus(), tk.GetPrev().String())
}
}
// RunTask is a http.Handler to run a Task from the "query string. // Run Task
// the request url likes /runtask?taskname=sendmail.
func runTask(rw http.ResponseWriter, req *http.Request) {
req.ParseForm() req.ParseForm()
taskname := req.Form.Get("taskname") taskname := req.Form.Get("taskname")
if t, ok := toolbox.AdminTaskList[taskname]; ok { if taskname != "" {
err := t.Run()
if err != nil { if t, ok := toolbox.AdminTaskList[taskname]; ok {
fmt.Fprintf(rw, "%v", err) err := t.Run()
if err != nil {
data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
}
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())}
} else {
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
} }
fmt.Fprintf(rw, "%s run success,Now the Status is %s", taskname, t.GetStatus())
} else {
fmt.Fprintf(rw, "there's no task which named:%s", taskname)
} }
// List Tasks
resultList := new([][]string)
var result = []string{
fmt.Sprintf("header"),
fmt.Sprintf("Task Name"),
fmt.Sprintf("Task Spec"),
fmt.Sprintf("Task Function"),
}
*resultList = append(*resultList, result)
for tname, tk := range toolbox.AdminTaskList {
result = []string{
fmt.Sprintf(""),
fmt.Sprintf("%s", tname),
fmt.Sprintf("%s", tk.GetStatus()),
fmt.Sprintf("%s", tk.GetPrev().String()),
}
*resultList = append(*resultList, result)
}
data["Content"] = resultList
data["Title"] = "Tasks"
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(tasksTpl))
tmpl.Execute(rw, data)
} }
// adminApp is an http.HandlerFunc map used as beeAdminApp. // adminApp is an http.HandlerFunc map used as beeAdminApp.

313
adminui.go Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,17 +1,12 @@
// Beego (http://beego.me/) // Beego (http://beego.me/)
//
// @description beego is an open-source, high-performance web framework for the Go programming language. // @description beego is an open-source, high-performance web framework for the Go programming language.
//
// @link http://github.com/astaxie/beego for the canonical source repository // @link http://github.com/astaxie/beego for the canonical source repository
//
// @license http://github.com/astaxie/beego/blob/master/LICENSE // @license http://github.com/astaxie/beego/blob/master/LICENSE
//
// @authors astaxie // @authors astaxie
package toolbox package toolbox
import ( import (
"fmt" "fmt"
"io"
"sync" "sync"
"time" "time"
) )
@ -79,17 +74,29 @@ func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController stri
} }
// put url statistics result in io.Writer // put url statistics result in io.Writer
func (m *UrlMap) GetMap(rw io.Writer) { func (m *UrlMap) GetMap() [][]string {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
fmt.Fprintf(rw, "| % -50s| % -10s | % -16s | % -16s | % -16s | % -16s | % -16s |\n", "requestUrl", "method", "times", "used", "max used", "min used", "avg used") resultLists := make([][]string, 0)
var result = []string{"requestUrl", "method", "times", "used", "max used", "min used", "avg used"}
resultLists = append(resultLists, result)
for k, v := range m.urlmap { for k, v := range m.urlmap {
for kk, vv := range v { for kk, vv := range v {
fmt.Fprintf(rw, "| % -50s| % -10s | % -16d | % -16s | % -16s | % -16s | % -16s |\n", k, result := []string{
kk, vv.RequestNum, toS(vv.TotalTime), toS(vv.MaxTime), toS(vv.MinTime), toS(time.Duration(int64(vv.TotalTime)/vv.RequestNum)), fmt.Sprintf("% -50s", k),
) fmt.Sprintf("% -10s", kk),
fmt.Sprintf("% -16d", vv.RequestNum),
fmt.Sprintf("% -16s", toS(vv.TotalTime)),
fmt.Sprintf("% -16s", toS(vv.MaxTime)),
fmt.Sprintf("% -16s", toS(vv.MinTime)),
fmt.Sprintf("% -16s", toS(time.Duration(int64(vv.TotalTime)/vv.RequestNum))),
}
resultLists = append(resultLists, result)
} }
} }
fmt.Println(resultLists)
return resultLists
} }
// global statistics data map // global statistics data map