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 }