github.com/lingyao2333/mo-zero@v1.4.1/core/prof/profilecenter.go (about)

     1  package prof
     2  
     3  import (
     4  	"bytes"
     5  	"strconv"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/lingyao2333/mo-zero/core/logx"
    11  	"github.com/lingyao2333/mo-zero/core/threading"
    12  	"github.com/olekukonko/tablewriter"
    13  )
    14  
    15  type (
    16  	profileSlot struct {
    17  		lifecount int64
    18  		lastcount int64
    19  		lifecycle int64
    20  		lastcycle int64
    21  	}
    22  
    23  	profileCenter struct {
    24  		lock  sync.RWMutex
    25  		slots map[string]*profileSlot
    26  	}
    27  )
    28  
    29  const flushInterval = 5 * time.Minute
    30  
    31  var (
    32  	pc = &profileCenter{
    33  		slots: make(map[string]*profileSlot),
    34  	}
    35  	once sync.Once
    36  )
    37  
    38  func report(name string, duration time.Duration) {
    39  	updated := func() bool {
    40  		pc.lock.RLock()
    41  		defer pc.lock.RUnlock()
    42  
    43  		slot, ok := pc.slots[name]
    44  		if ok {
    45  			atomic.AddInt64(&slot.lifecount, 1)
    46  			atomic.AddInt64(&slot.lastcount, 1)
    47  			atomic.AddInt64(&slot.lifecycle, int64(duration))
    48  			atomic.AddInt64(&slot.lastcycle, int64(duration))
    49  		}
    50  		return ok
    51  	}()
    52  
    53  	if !updated {
    54  		func() {
    55  			pc.lock.Lock()
    56  			defer pc.lock.Unlock()
    57  
    58  			pc.slots[name] = &profileSlot{
    59  				lifecount: 1,
    60  				lastcount: 1,
    61  				lifecycle: int64(duration),
    62  				lastcycle: int64(duration),
    63  			}
    64  		}()
    65  	}
    66  
    67  	once.Do(flushRepeatly)
    68  }
    69  
    70  func flushRepeatly() {
    71  	threading.GoSafe(func() {
    72  		for {
    73  			time.Sleep(flushInterval)
    74  			logx.Stat(generateReport())
    75  		}
    76  	})
    77  }
    78  
    79  func generateReport() string {
    80  	var buffer bytes.Buffer
    81  	buffer.WriteString("Profiling report\n")
    82  	var data [][]string
    83  	calcFn := func(total, count int64) string {
    84  		if count == 0 {
    85  			return "-"
    86  		}
    87  
    88  		return (time.Duration(total) / time.Duration(count)).String()
    89  	}
    90  
    91  	func() {
    92  		pc.lock.Lock()
    93  		defer pc.lock.Unlock()
    94  
    95  		for key, slot := range pc.slots {
    96  			data = append(data, []string{
    97  				key,
    98  				strconv.FormatInt(slot.lifecount, 10),
    99  				calcFn(slot.lifecycle, slot.lifecount),
   100  				strconv.FormatInt(slot.lastcount, 10),
   101  				calcFn(slot.lastcycle, slot.lastcount),
   102  			})
   103  
   104  			// reset the data for last cycle
   105  			slot.lastcount = 0
   106  			slot.lastcycle = 0
   107  		}
   108  	}()
   109  
   110  	table := tablewriter.NewWriter(&buffer)
   111  	table.SetHeader([]string{"QUEUE", "LIFECOUNT", "LIFECYCLE", "LASTCOUNT", "LASTCYCLE"})
   112  	table.SetBorder(false)
   113  	table.AppendBulk(data)
   114  	table.Render()
   115  
   116  	return buffer.String()
   117  }