package statutils import ( "encoding/json" "fmt" "runtime" "sort" "stat/common/utils" "sync" "time" "unsafe" ) var ServerMon = Monitor{} type ErrorRecord struct { Error error `json:"error"` Occur time.Time `json:"occur"` } type JobItem struct { Name string `json:"name"` Call string `json:"call"` Count int64 `json:"count"` Start time.Time `json:"start"` Stop time.Time `json:"stop"` Avg time.Duration `json:"avg"` Gap time.Duration `json:"gap"` Error error `json:"error"` SuccCount int64 `json:"succ_count"` ErrorCount int64 `json:"error_count"` Errors []*ErrorRecord `json:"errors"` } func SpendTimeIcon(d time.Duration) string { if d < 5*time.Second { return "\U0001F7E2\U0001F7E2\U0001F7E2" } else if d < 10*time.Second { return "\U0001F7E2\U0001F7E2\U0001F7E0" } else if d < 30*time.Second { return "\U0001F7E2\U0001F7E0\U0001F7E0" } else if d < 80*time.Second { return "\U0001F7E0\U0001F7E0\U0001F7E0" } else if d < 220*time.Second { return "\U0001F7E0\U0001F7E0πŸ”΄" } else if d < 600*time.Second { return "\U0001F7E0πŸ”΄πŸ”΄" } else if d < 1640*time.Second { return "πŸ”΄πŸ”΄πŸ”΄" } else { return "πŸ†˜πŸ†˜πŸ†˜" } } func (s *JobItem) Status() string { if s.Count > s.SuccCount+s.ErrorCount { return "πŸ’›ζ‰§θ‘ŒδΈ­" } else { if s.Error != nil { return "πŸ’”ζ‰§θ‘Œε‡Ίι”™" } else { return "πŸ’šζ‰§θ‘ŒζˆεŠŸ" } } } func (s *JobItem) Display() string { text := fmt.Sprintf("【函数】%s\n", s.Name) text += fmt.Sprintf("【调用】%s\n", s.Call) text += fmt.Sprintf("【进ε…₯】%d欑\n", s.Count) text += fmt.Sprintf("γ€ηŠΆζ€γ€‘%s\n", s.Status()) text += fmt.Sprintf("【开始】%s\n", s.Start.Format("15:04:05")) text += fmt.Sprintf("γ€η»“ζŸγ€‘%s\n", s.Stop.Format("15:04:05")) text += fmt.Sprintf("【用既】%s\n", s.Stop.Sub(s.Start)) text += fmt.Sprintf("γ€ι€ŸεΊ¦γ€‘%s\n", SpendTimeIcon(s.Stop.Sub(s.Start))) text += fmt.Sprintf("【平均】%s\n", s.Avg) text += fmt.Sprintf("γ€ι—΄ιš”γ€‘%s\n", s.Gap) text += fmt.Sprintf("γ€ζˆεŠŸγ€‘%d欑\n", s.SuccCount) text += fmt.Sprintf("【出错】%d欑\n", s.ErrorCount) if len(s.Errors) > 0 { text += fmt.Sprintf("γ€ζœ€ε‰ε‡Ίι”™δΏ‘ζ―γ€‘πŸ”»%s\n", s.Errors[0].Error.Error()) } if len(s.Errors) > 1 { text += fmt.Sprintf("γ€ζœ€εŽε‡Ίι”™δΏ‘ζ―γ€‘πŸ”Ί%s\n", s.Errors[len(s.Errors)-1].Error.Error()) } return text } func newJob(name string, call string) *JobItem { now := time.Now() return &JobItem{ Name: name, Call: call, Count: 1, Start: now, Stop: now, Errors: make([]*ErrorRecord, 0), } } func (s *JobItem) Begin() { now := time.Now() if s.Count > 0 { s.Gap = now.Sub(s.Start) } s.Start = now s.Count++ } func (s *JobItem) End(err error) { now := time.Now() s.Error = err s.Stop = now if err != nil { s.ErrorCount++ s.Errors = append(s.Errors, &ErrorRecord{ Error: err, Occur: now, }) } else { s.SuccCount++ s.Avg = time.Duration(int64(s.Avg+(s.Stop.Sub(s.Start))) / s.SuccCount) } } type Monitor struct { lock sync.Mutex jobs map[string]*JobItem } func (s *Monitor) call(callFunc func() error, funcName string, callName string) error { func() { s.lock.Lock() defer s.lock.Unlock() if s.jobs == nil { s.jobs = make(map[string]*JobItem) } job, ok := s.jobs[funcName] if !ok { job = newJob(funcName, callName) s.jobs[funcName] = job } else { job.Begin() } }() err := callFunc() func() { s.lock.Lock() defer s.lock.Unlock() job := s.jobs[funcName] if job != nil { job.End(err) } }() return err } func (s *Monitor) Run(fn func() error) error { defer utils.RecoverError(nil) callName := GetCallName(2) funcName := GetFuncName(fn) call := func() (err error) { defer utils.RecoverError(func(err1 error) { err = err1 }) err = fn() return err } return s.call(call, funcName, callName) } func (s *Monitor) RunWithName(fn func() error, funcName string) error { defer utils.RecoverError(nil) callName := GetCallName(2) call := func() (err error) { defer utils.RecoverError(func(err1 error) { err = err1 }) err = fn() return err } return s.call(call, funcName, callName) } func (s *Monitor) Run1(fn func(v time.Time) error, param time.Time) error { defer utils.RecoverError(nil) callName := GetCallName(2) funcName := GetFuncName(fn) call := func() (err error) { defer utils.RecoverError(func(err1 error) { err = err1 }) err = fn(param) return err } return s.call(call, funcName, callName) } func (s *Monitor) ToJson() string { s.lock.Lock() defer s.lock.Unlock() data, _ := json.Marshal(s.jobs) return string(data) } func (s *Monitor) Display() string { s.lock.Lock() defer s.lock.Unlock() list := make(JobItemList, 0) for _, job := range s.jobs { list = append(list, job) } sort.Sort(list) text := fmt.Sprintf("后台θ‡ͺ动任劑(%dδΈͺ):\n", len(s.jobs)) for _, job := range list { text += job.Display() text += "***** *****\n" } return text } func GetCallName(skip int) string { pc, _, _, _ := runtime.Caller(skip) return runtime.FuncForPC(pc).Name() } func GetFuncName(fn interface{}) string { funcPC := func(fn interface{}) uintptr { ptr := (*[2]uintptr)(unsafe.Pointer(&fn))[1] return **((**uintptr)(unsafe.Pointer(&ptr))) } pc := funcPC(fn) return runtime.FuncForPC(pc).Name() } type JobItemList []*JobItem func (a JobItemList) Len() int { return len(a) } func (a JobItemList) Less(i, j int) bool { return a[i].Name < a[j].Name } func (a JobItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }