github.com/wfusion/gofusion@v1.1.14/common/infra/metrics/inmem_signal.go (about)

     1  package metrics
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/signal"
     9  	"strings"
    10  	"sync"
    11  	"syscall"
    12  )
    13  
    14  // InmemSignal is used to listen for a given signal, and when received,
    15  // to dump the current metrics from the InmemSink to an io.Writer
    16  type InmemSignal struct {
    17  	signal syscall.Signal
    18  	inm    *InmemSink
    19  	w      io.Writer
    20  	sigCh  chan os.Signal
    21  
    22  	stop     bool
    23  	stopCh   chan struct{}
    24  	stopLock sync.Mutex
    25  }
    26  
    27  // NewInmemSignal creates a new InmemSignal which listens for a given signal,
    28  // and dumps the current metrics out to a writer
    29  func NewInmemSignal(inmem *InmemSink, sig syscall.Signal, w io.Writer) *InmemSignal {
    30  	i := &InmemSignal{
    31  		signal: sig,
    32  		inm:    inmem,
    33  		w:      w,
    34  		sigCh:  make(chan os.Signal, 1),
    35  		stopCh: make(chan struct{}),
    36  	}
    37  	signal.Notify(i.sigCh, sig)
    38  	go i.run()
    39  	return i
    40  }
    41  
    42  // DefaultInmemSignal returns a new InmemSignal that responds to SIGUSR1
    43  // and writes output to stderr. Windows uses SIGBREAK
    44  func DefaultInmemSignal(inmem *InmemSink) *InmemSignal {
    45  	return NewInmemSignal(inmem, DefaultSignal, os.Stderr)
    46  }
    47  
    48  // Stop is used to stop the InmemSignal from listening
    49  func (i *InmemSignal) Stop() {
    50  	i.stopLock.Lock()
    51  	defer i.stopLock.Unlock()
    52  
    53  	if i.stop {
    54  		return
    55  	}
    56  	i.stop = true
    57  	close(i.stopCh)
    58  	signal.Stop(i.sigCh)
    59  }
    60  
    61  // run is a long running routine that handles signals
    62  func (i *InmemSignal) run() {
    63  	for {
    64  		select {
    65  		case <-i.sigCh:
    66  			i.dumpStats()
    67  		case <-i.stopCh:
    68  			return
    69  		}
    70  	}
    71  }
    72  
    73  // dumpStats is used to dump the data to output writer
    74  func (i *InmemSignal) dumpStats() {
    75  	buf := bytes.NewBuffer(nil)
    76  
    77  	data := i.inm.Data()
    78  	// Skip the last period which is still being aggregated
    79  	for j := 0; j < len(data)-1; j++ {
    80  		intv := data[j]
    81  		intv.RLock()
    82  		for _, val := range intv.Gauges {
    83  			name := i.flattenLabels(val.Name, val.Labels)
    84  			fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value)
    85  		}
    86  		for _, val := range intv.PrecisionGauges {
    87  			name := i.flattenLabels(val.Name, val.Labels)
    88  			fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value)
    89  		}
    90  		for name, vals := range intv.Points {
    91  			for _, val := range vals {
    92  				fmt.Fprintf(buf, "[%v][P] '%s': %0.3f\n", intv.Interval, name, val)
    93  			}
    94  		}
    95  		for _, agg := range intv.Counters {
    96  			name := i.flattenLabels(agg.Name, agg.Labels)
    97  			fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg.AggregateSample)
    98  		}
    99  		for _, agg := range intv.Samples {
   100  			name := i.flattenLabels(agg.Name, agg.Labels)
   101  			fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg.AggregateSample)
   102  		}
   103  		intv.RUnlock()
   104  	}
   105  
   106  	// Write out the bytes
   107  	i.w.Write(buf.Bytes())
   108  }
   109  
   110  // Flattens the key for formatting along with its labels, removes spaces
   111  func (i *InmemSignal) flattenLabels(name string, labels []Label) string {
   112  	buf := bytes.NewBufferString(name)
   113  	replacer := strings.NewReplacer(" ", "_", ":", "_")
   114  
   115  	for _, label := range labels {
   116  		replacer.WriteString(buf, ".")
   117  		replacer.WriteString(buf, label.Value)
   118  	}
   119  
   120  	return buf.String()
   121  }