244 lines
5.5 KiB
Go
244 lines
5.5 KiB
Go
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] }
|