forked from all_user/stat_center
Initial commit
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
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] }
|
||||
Reference in New Issue
Block a user