github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/monitor/monitor.go (about)

     1  package monitor
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"path/filepath"
     7  	"sort"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/codingeasygo/util/xmap"
    12  	"github.com/codingeasygo/util/xtime"
    13  )
    14  
    15  type Statable interface {
    16  	State() (interface{}, error)
    17  }
    18  
    19  type State struct {
    20  	Name    string `json:"name"`
    21  	Min     int64  `json:"min"`
    22  	Max     int64  `json:"max"`
    23  	Total   int64  `json:"total"`
    24  	Count   int64  `json:"count"`
    25  	ConcMax int64  `json:"con_max"`
    26  	ConcAvg int64  `json:"con_avg"`
    27  	//
    28  	concAll   uint64
    29  	concCount uint64
    30  }
    31  
    32  type Monitor struct {
    33  	Used     map[string]*State
    34  	Pending  map[string]int64
    35  	max      map[string]int64
    36  	lck      sync.RWMutex
    37  	sequence uint64
    38  }
    39  
    40  func New() *Monitor {
    41  	return &Monitor{
    42  		Used:    map[string]*State{},
    43  		Pending: map[string]int64{},
    44  		max:     map[string]int64{},
    45  		lck:     sync.RWMutex{},
    46  	}
    47  }
    48  func (m *Monitor) Start(name string) string {
    49  	m.lck.Lock()
    50  	defer m.lck.Unlock()
    51  	m.sequence++
    52  	var id = fmt.Sprintf("%v/%v", name, m.sequence)
    53  	m.Pending[id] = xtime.Now()
    54  	m.max[name]++
    55  	old, ok := m.Used[name]
    56  	if !ok {
    57  		old = &State{Name: name, Min: math.MaxInt64}
    58  	}
    59  	old.concAll += uint64(m.max[name])
    60  	old.concCount++
    61  	old.ConcAvg = int64(old.concAll / old.concCount)
    62  	if old.ConcMax < m.max[name] {
    63  		old.ConcMax = m.max[name]
    64  	}
    65  	return id
    66  }
    67  
    68  func (m *Monitor) Start_(id string) {
    69  	m.lck.Lock()
    70  	defer m.lck.Unlock()
    71  	m.Pending[id] = xtime.Now()
    72  }
    73  
    74  func (m *Monitor) Done(id string) {
    75  	m.lck.Lock()
    76  	defer m.lck.Unlock()
    77  	beg, ok := m.Pending[id]
    78  	if !ok {
    79  		return
    80  	}
    81  	delete(m.Pending, id)
    82  	name := filepath.Dir(id)
    83  	name = strings.TrimSuffix(name, "/")
    84  	old, ok := m.Used[name]
    85  	if !ok {
    86  		old = &State{Name: name, Min: math.MaxInt64}
    87  	}
    88  	used := xtime.Now() - beg
    89  	old.Total += used
    90  	old.Count++
    91  	if old.Max < used {
    92  		old.Max = used
    93  	}
    94  	if old.Min > used {
    95  		old.Min = used
    96  	}
    97  	m.Used[name] = old
    98  	m.max[name]--
    99  }
   100  
   101  func (m *Monitor) State() (interface{}, error) {
   102  	m.lck.RLock()
   103  	defer m.lck.RUnlock()
   104  	var used = []xmap.M{}
   105  	for _, u := range m.Used {
   106  		used = append(used, xmap.M{
   107  			"name":     u.Name,
   108  			"min":      u.Min,
   109  			"max":      u.Max,
   110  			"total":    u.Total,
   111  			"count":    u.Count,
   112  			"avg":      u.Total / u.Count,
   113  			"conc_max": u.ConcMax,
   114  			"conc_avg": u.ConcAvg,
   115  		})
   116  	}
   117  	sort.Sort(xmap.NewMSorter(xmap.WrapArray(used), 0, true, "/avg"))
   118  	//
   119  	var pending = map[string]int64{}
   120  	for k, v := range m.Pending {
   121  		pending[k] = v
   122  	}
   123  	//
   124  	return xmap.M{
   125  		"used":    used,
   126  		"pending": pending,
   127  	}, nil
   128  }