6c13bdde by astaxie

support profile & statistics in another port

1 parent a981dab5
1 package beego
2
3 import (
4 "fmt"
5 "net/http"
6 )
7
8 var BeeAdminApp *AdminApp
9
10 func init() {
11 BeeAdminApp = &AdminApp{
12 routers: make(map[string]http.HandlerFunc),
13 }
14 BeeAdminApp.Route("/", AdminIndex)
15 }
16
17 func AdminIndex(rw http.ResponseWriter, r *http.Request) {
18 rw.Write([]byte("Welcome to Admin Dashboard"))
19 }
20
21 type AdminApp struct {
22 routers map[string]http.HandlerFunc
23 }
24
25 func (admin *AdminApp) Route(pattern string, f http.HandlerFunc) {
26 admin.routers[pattern] = f
27 }
28
29 func (admin *AdminApp) Run() {
30 addr := AdminHttpAddr
31
32 if AdminHttpPort != 0 {
33 addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort)
34 }
35 for p, f := range admin.routers {
36 http.Handle(p, f)
37 }
38 err := http.ListenAndServe(addr, nil)
39 if err != nil {
40 BeeLogger.Critical("Admin ListenAndServe: ", err)
41 }
42 }
1 package admin
2
3 import (
4 "fmt"
5 "log"
6 "os"
7 "runtime"
8 "runtime/debug"
9 "runtime/pprof"
10 "strconv"
11 "time"
12 )
13
14 var heapProfileCounter int32
15 var startTime = time.Now()
16 var pid int
17
18 func init() {
19 pid = os.Getpid()
20 }
21
22 func ProcessInput(input string) {
23 switch input {
24 case "lookup goroutine":
25 p := pprof.Lookup("goroutine")
26 p.WriteTo(os.Stdout, 2)
27 case "lookup heap":
28 p := pprof.Lookup("heap")
29 p.WriteTo(os.Stdout, 2)
30 case "lookup threadcreate":
31 p := pprof.Lookup("threadcreate")
32 p.WriteTo(os.Stdout, 2)
33 case "lookup block":
34 p := pprof.Lookup("block")
35 p.WriteTo(os.Stdout, 2)
36 case "start cpuprof":
37 StartCPUProfile()
38 case "stop cpuprof":
39 StopCPUProfile()
40 case "get memprof":
41 MemProf()
42 case "gc summary":
43 PrintGCSummary()
44 }
45 }
46
47 func MemProf() {
48 if f, err := os.Create("mem-" + strconv.Itoa(pid) + ".memprof"); err != nil {
49 log.Fatal("record memory profile failed: %v", err)
50 } else {
51 runtime.GC()
52 pprof.WriteHeapProfile(f)
53 f.Close()
54 }
55 }
56
57 func StartCPUProfile() {
58 f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
59 if err != nil {
60 log.Fatal(err)
61 }
62 pprof.StartCPUProfile(f)
63 }
64
65 func StopCPUProfile() {
66 pprof.StopCPUProfile()
67 }
68
69 func PrintGCSummary() {
70 memStats := &runtime.MemStats{}
71 runtime.ReadMemStats(memStats)
72 gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
73 debug.ReadGCStats(gcstats)
74
75 printGC(memStats, gcstats)
76 }
77
78 func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats) {
79
80 if gcstats.NumGC > 0 {
81 lastPause := gcstats.Pause[0]
82 elapsed := time.Now().Sub(startTime)
83 overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100
84 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
85
86 fmt.Printf("NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n",
87 gcstats.NumGC,
88 toS(lastPause),
89 toS(avg(gcstats.Pause)),
90 overhead,
91 toH(memStats.Alloc),
92 toH(memStats.Sys),
93 toH(uint64(allocatedRate)),
94 toS(gcstats.PauseQuantiles[94]),
95 toS(gcstats.PauseQuantiles[98]),
96 toS(gcstats.PauseQuantiles[99]))
97 } else {
98 // while GC has disabled
99 elapsed := time.Now().Sub(startTime)
100 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
101
102 fmt.Printf("Alloc:%s Sys:%s Alloc(Rate):%s/s\n",
103 toH(memStats.Alloc),
104 toH(memStats.Sys),
105 toH(uint64(allocatedRate)))
106 }
107 }
108
109 func avg(items []time.Duration) time.Duration {
110 var sum time.Duration
111 for _, item := range items {
112 sum += item
113 }
114 return time.Duration(int64(sum) / int64(len(items)))
115 }
116
117 // human readable format
118 func toH(bytes uint64) string {
119 switch {
120 case bytes < 1024:
121 return fmt.Sprintf("%dB", bytes)
122 case bytes < 1024*1024:
123 return fmt.Sprintf("%.2fK", float64(bytes)/1024)
124 case bytes < 1024*1024*1024:
125 return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024)
126 default:
127 return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024)
128 }
129 }
130
131 // short string format
132 func toS(d time.Duration) string {
133
134 u := uint64(d)
135 if u < uint64(time.Second) {
136 switch {
137 case u == 0:
138 return "0"
139 case u < uint64(time.Microsecond):
140 return fmt.Sprintf("%.2fns", float64(u))
141 case u < uint64(time.Millisecond):
142 return fmt.Sprintf("%.2fus", float64(u)/1000)
143 default:
144 return fmt.Sprintf("%.2fms", float64(u)/1000/1000)
145 }
146 } else {
147 switch {
148 case u < uint64(time.Minute):
149 return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000)
150 case u < uint64(time.Hour):
151 return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60)
152 default:
153 return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60)
154 }
155 }
156
157 }
1 package admin
2
3 import (
4 "encoding/json"
5 "sync"
6 "time"
7 )
8
9 type Statistics struct {
10 RequestUrl string
11 RequestController string
12 RequestNum int64
13 MinTime time.Duration
14 MaxTime time.Duration
15 TotalTime time.Duration
16 }
17
18 type UrlMap struct {
19 lock sync.RWMutex
20 urlmap map[string]map[string]*Statistics
21 }
22
23 func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController string, requesttime time.Duration) {
24 m.lock.Lock()
25 defer m.lock.Unlock()
26 if method, ok := m.urlmap[requestUrl]; ok {
27 if s, ok := method[requestMethod]; ok {
28 s.RequestNum += 1
29 if s.MaxTime < requesttime {
30 s.MaxTime = requesttime
31 }
32 if s.MinTime > requesttime {
33 s.MinTime = requesttime
34 }
35 s.TotalTime += requesttime
36 } else {
37 nb := &Statistics{
38 RequestUrl: requestUrl,
39 RequestController: requestController,
40 RequestNum: 1,
41 MinTime: requesttime,
42 MaxTime: requesttime,
43 TotalTime: requesttime,
44 }
45 m.urlmap[requestUrl][requestMethod] = nb
46 }
47
48 } else {
49 methodmap := make(map[string]*Statistics)
50 nb := &Statistics{
51 RequestUrl: requestUrl,
52 RequestController: requestController,
53 RequestNum: 1,
54 MinTime: requesttime,
55 MaxTime: requesttime,
56 TotalTime: requesttime,
57 }
58 methodmap[requestMethod] = nb
59 m.urlmap[requestUrl] = methodmap
60 }
61 }
62
63 func (m *UrlMap) GetMap() []byte {
64 m.lock.RLock()
65 defer m.lock.RUnlock()
66 r, err := json.Marshal(m.urlmap)
67 if err != nil {
68 return []byte("")
69 }
70 return r
71 }
72
73 var StatisticsMap *UrlMap
74
75 func init() {
76 StatisticsMap = &UrlMap{
77 urlmap: make(map[string]map[string]*Statistics),
78 }
79 }
1 package admin
2
3 import (
4 "testing"
5 "time"
6 )
7
8 func TestStatics(t *testing.T) {
9 StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(1000000))
10 StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(1200000))
11 StatisticsMap.AddStatistics("GET", "/api/user", "&admin.user", time.Duration(1300000))
12 StatisticsMap.AddStatistics("POST", "/api/admin", "&admin.user", time.Duration(1400000))
13 StatisticsMap.AddStatistics("POST", "/api/user/astaxie", "&admin.user", time.Duration(1200000))
14 StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(1300000))
15 StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400000))
16 s := StatisticsMap.GetMap()
17 }
1 package admin
...\ No newline at end of file ...\ No newline at end of file
...@@ -93,5 +93,9 @@ func Run() { ...@@ -93,5 +93,9 @@ func Run() {
93 middleware.AppName = AppName 93 middleware.AppName = AppName
94 middleware.RegisterErrorHander() 94 middleware.RegisterErrorHander()
95 95
96 if EnableAdmin {
97 go BeeAdminApp.Run()
98 }
99
96 BeeApp.Run() 100 BeeApp.Run()
97 } 101 }
......
...@@ -53,6 +53,9 @@ var ( ...@@ -53,6 +53,9 @@ var (
53 TemplateLeft string 53 TemplateLeft string
54 TemplateRight string 54 TemplateRight string
55 BeegoServerName string 55 BeegoServerName string
56 EnableAdmin bool //enable admin module to log api time
57 AdminHttpAddr string //admin module http addr
58 AdminHttpPort int
56 ) 59 )
57 60
58 func init() { 61 func init() {
...@@ -89,6 +92,9 @@ func init() { ...@@ -89,6 +92,9 @@ func init() {
89 TemplateLeft = "{{" 92 TemplateLeft = "{{"
90 TemplateRight = "}}" 93 TemplateRight = "}}"
91 BeegoServerName = "beegoServer" 94 BeegoServerName = "beegoServer"
95 EnableAdmin = true
96 AdminHttpAddr = "localhost"
97 AdminHttpPort = 8088
92 ParseConfig() 98 ParseConfig()
93 runtime.GOMAXPROCS(runtime.NumCPU()) 99 runtime.GOMAXPROCS(runtime.NumCPU())
94 } 100 }
...@@ -311,6 +317,24 @@ func ParseConfig() (err error) { ...@@ -311,6 +317,24 @@ func ParseConfig() (err error) {
311 } 317 }
312 } 318 }
313 } 319 }
320 if enableadmin, err := AppConfig.Bool("enableadmin"); err == nil {
321 EnableAdmin = enableadmin
322 }
323 if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil {
324 EnableAdmin = enableadmin
325 }
326 if adminhttpaddr := AppConfig.String("admintttpaddr"); adminhttpaddr != "" {
327 AdminHttpAddr = adminhttpaddr
328 }
329 if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" {
330 AdminHttpAddr = adminhttpaddr
331 }
332 if adminhttpport, err := AppConfig.Int("adminhttpport"); err == nil {
333 AdminHttpPort = adminhttpport
334 }
335 if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil {
336 AdminHttpPort = adminhttpport
337 }
314 } 338 }
315 return nil 339 return nil
316 } 340 }
......
...@@ -258,3 +258,7 @@ func stringsToJson(str string) string { ...@@ -258,3 +258,7 @@ func stringsToJson(str string) string {
258 } 258 }
259 return jsons 259 return jsons
260 } 260 }
261
262 func (output *BeegoOutput) Session(name interface{}, value interface{}) {
263 output.Context.Input.CruSession.Set(name, value)
264 }
......
...@@ -2,6 +2,7 @@ package beego ...@@ -2,6 +2,7 @@ package beego
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
5 "github.com/astaxie/beego/admin"
5 beecontext "github.com/astaxie/beego/context" 6 beecontext "github.com/astaxie/beego/context"
6 "github.com/astaxie/beego/middleware" 7 "github.com/astaxie/beego/middleware"
7 "net/http" 8 "net/http"
...@@ -12,6 +13,7 @@ import ( ...@@ -12,6 +13,7 @@ import (
12 "runtime" 13 "runtime"
13 "strconv" 14 "strconv"
14 "strings" 15 "strings"
16 "time"
15 ) 17 )
16 18
17 var HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head"} 19 var HTTPMETHOD = []string{"get", "post", "put", "delete", "patch", "options", "head"}
...@@ -406,6 +408,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -406,6 +408,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
406 } 408 }
407 }() 409 }()
408 410
411 starttime := time.Now()
412 requestPath := r.URL.Path
413 var runrouter *controllerInfo
414 var findrouter bool
415 params := make(map[string]string)
416
409 w := &responseWriter{writer: rw} 417 w := &responseWriter{writer: rw}
410 w.Header().Set("Server", BeegoServerName) 418 w.Header().Set("Server", BeegoServerName)
411 context := &beecontext.Context{ 419 context := &beecontext.Context{
...@@ -422,27 +430,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -422,27 +430,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
422 context.Output = beecontext.NewOutput(rw) 430 context.Output = beecontext.NewOutput(rw)
423 } 431 }
424 432
425 if SessionOn {
426 context.Input.CruSession = GlobalSessions.SessionStart(w, r)
427 }
428
429 if !inSlice(strings.ToLower(r.Method), HTTPMETHOD) { 433 if !inSlice(strings.ToLower(r.Method), HTTPMETHOD) {
430 http.Error(w, "Method Not Allowed", 405) 434 http.Error(w, "Method Not Allowed", 405)
431 return 435 goto Admin
432 } 436 }
433 437
434 var runrouter *controllerInfo
435 var findrouter bool
436
437 params := make(map[string]string)
438
439 if p.enableFilter { 438 if p.enableFilter {
440 if l, ok := p.filters["BeforRouter"]; ok { 439 if l, ok := p.filters["BeforRouter"]; ok {
441 for _, filterR := range l { 440 for _, filterR := range l {
442 if filterR.ValidRouter(r.URL.Path) { 441 if filterR.ValidRouter(r.URL.Path) {
443 filterR.filterFunc(context) 442 filterR.filterFunc(context)
444 if w.started { 443 if w.started {
445 return 444 goto Admin
446 } 445 }
447 } 446 }
448 } 447 }
...@@ -455,7 +454,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -455,7 +454,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
455 file := staticDir + r.URL.Path 454 file := staticDir + r.URL.Path
456 http.ServeFile(w, r, file) 455 http.ServeFile(w, r, file)
457 w.started = true 456 w.started = true
458 return 457 goto Admin
459 } 458 }
460 if strings.HasPrefix(r.URL.Path, prefix) { 459 if strings.HasPrefix(r.URL.Path, prefix) {
461 file := staticDir + r.URL.Path[len(prefix):] 460 file := staticDir + r.URL.Path[len(prefix):]
...@@ -465,32 +464,36 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -465,32 +464,36 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
465 Warn(err) 464 Warn(err)
466 } 465 }
467 http.NotFound(w, r) 466 http.NotFound(w, r)
468 return 467 goto Admin
469 } 468 }
470 //if the request is dir and DirectoryIndex is false then 469 //if the request is dir and DirectoryIndex is false then
471 if finfo.IsDir() && !DirectoryIndex { 470 if finfo.IsDir() && !DirectoryIndex {
472 middleware.Exception("403", rw, r, "403 Forbidden") 471 middleware.Exception("403", rw, r, "403 Forbidden")
473 return 472 goto Admin
474 } 473 }
475 http.ServeFile(w, r, file) 474 http.ServeFile(w, r, file)
476 w.started = true 475 w.started = true
477 return 476 goto Admin
478 } 477 }
479 } 478 }
480 479
480 // session init after static file
481 if SessionOn {
482 context.Input.CruSession = GlobalSessions.SessionStart(w, r)
483 }
484
481 if p.enableFilter { 485 if p.enableFilter {
482 if l, ok := p.filters["AfterStatic"]; ok { 486 if l, ok := p.filters["AfterStatic"]; ok {
483 for _, filterR := range l { 487 for _, filterR := range l {
484 if filterR.ValidRouter(r.URL.Path) { 488 if filterR.ValidRouter(r.URL.Path) {
485 filterR.filterFunc(context) 489 filterR.filterFunc(context)
486 if w.started { 490 if w.started {
487 return 491 goto Admin
488 } 492 }
489 } 493 }
490 } 494 }
491 } 495 }
492 } 496 }
493 requestPath := r.URL.Path
494 497
495 if CopyRequestBody { 498 if CopyRequestBody {
496 context.Input.Body() 499 context.Input.Body()
...@@ -509,7 +512,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -509,7 +512,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
509 if requestPath[n-1] != '/' && len(route.pattern) == n+1 && 512 if requestPath[n-1] != '/' && len(route.pattern) == n+1 &&
510 route.pattern[n] == '/' && route.pattern[:n] == requestPath { 513 route.pattern[n] == '/' && route.pattern[:n] == requestPath {
511 http.Redirect(w, r, requestPath+"/", 301) 514 http.Redirect(w, r, requestPath+"/", 301)
512 return 515 goto Admin
513 } 516 }
514 } 517 }
515 518
...@@ -546,6 +549,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -546,6 +549,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
546 break 549 break
547 } 550 }
548 } 551 }
552
549 context.Input.Param = params 553 context.Input.Param = params
550 554
551 if runrouter != nil { 555 if runrouter != nil {
...@@ -559,7 +563,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -559,7 +563,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
559 if filterR.ValidRouter(r.URL.Path) { 563 if filterR.ValidRouter(r.URL.Path) {
560 filterR.filterFunc(context) 564 filterR.filterFunc(context)
561 if w.started { 565 if w.started {
562 return 566 goto Admin
563 } 567 }
564 } 568 }
565 } 569 }
...@@ -714,7 +718,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -714,7 +718,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
714 if filterR.ValidRouter(r.URL.Path) { 718 if filterR.ValidRouter(r.URL.Path) {
715 filterR.filterFunc(context) 719 filterR.filterFunc(context)
716 if w.started { 720 if w.started {
717 return 721 goto Admin
718 } 722 }
719 } 723 }
720 } 724 }
...@@ -740,7 +744,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -740,7 +744,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
740 744
741 if strings.ToLower(requestPath) == "/"+cName { 745 if strings.ToLower(requestPath) == "/"+cName {
742 http.Redirect(w, r, requestPath+"/", 301) 746 http.Redirect(w, r, requestPath+"/", 301)
743 return 747 goto Admin
744 } 748 }
745 749
746 if strings.ToLower(requestPath) == "/"+cName+"/" { 750 if strings.ToLower(requestPath) == "/"+cName+"/" {
...@@ -754,6 +758,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -754,6 +758,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
754 if r.Method == "POST" { 758 if r.Method == "POST" {
755 r.ParseMultipartForm(MaxMemory) 759 r.ParseMultipartForm(MaxMemory)
756 } 760 }
761 // set find
762 findrouter = true
757 //execute middleware filters 763 //execute middleware filters
758 if p.enableFilter { 764 if p.enableFilter {
759 if l, ok := p.filters["BeforExec"]; ok { 765 if l, ok := p.filters["BeforExec"]; ok {
...@@ -761,7 +767,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -761,7 +767,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
761 if filterR.ValidRouter(r.URL.Path) { 767 if filterR.ValidRouter(r.URL.Path) {
762 filterR.filterFunc(context) 768 filterR.filterFunc(context)
763 if w.started { 769 if w.started {
764 return 770 goto Admin
765 } 771 }
766 } 772 }
767 } 773 }
...@@ -816,7 +822,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -816,7 +822,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
816 if filterR.ValidRouter(r.URL.Path) { 822 if filterR.ValidRouter(r.URL.Path) {
817 filterR.filterFunc(context) 823 filterR.filterFunc(context)
818 if w.started { 824 if w.started {
819 return 825 goto Admin
820 } 826 }
821 } 827 }
822 } 828 }
...@@ -824,9 +830,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -824,9 +830,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
824 } 830 }
825 method = vc.MethodByName("Destructor") 831 method = vc.MethodByName("Destructor")
826 method.Call(in) 832 method.Call(in)
827 // set find 833 goto Admin
828 findrouter = true
829 goto Last
830 } 834 }
831 } 835 }
832 } 836 }
...@@ -834,11 +838,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -834,11 +838,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
834 } 838 }
835 } 839 }
836 840
837 Last:
838 //if no matches to url, throw a not found exception 841 //if no matches to url, throw a not found exception
839 if !findrouter { 842 if !findrouter {
840 middleware.Exception("404", rw, r, "") 843 middleware.Exception("404", rw, r, "")
841 } 844 }
845
846 Admin:
847 //admin module record QPS
848 if EnableAdmin {
849 go admin.StatisticsMap.AddStatistics(r.Method, requestPath, runrouter.controllerType.Name(), time.Since(starttime))
850 }
842 } 851 }
843 852
844 //responseWriter is a wrapper for the http.ResponseWriter 853 //responseWriter is a wrapper for the http.ResponseWriter
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!