github.com/GuanceCloud/cliutils@v1.1.21/network/http/api_metric.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 package http 7 8 import ( 9 "time" 10 ) 11 12 var ( 13 metricCh = make(chan *APIMetric, 32) 14 qch = make(chan *qAPIStats) 15 exitch = make(chan interface{}) 16 ) 17 18 type APIMetric struct { 19 API string 20 Latency time.Duration 21 StatusCode int 22 Limited bool 23 } 24 25 // APIMetricReporter used to collects API metrics during API handing. 26 type APIMetricReporter interface { 27 Report(*APIMetric) // report these metrics 28 } 29 30 // ReporterImpl used to report API stats 31 // TODO: We should implemente a default API metric reporter under cliutils. 32 type ReporterImpl struct{} 33 34 func (r *ReporterImpl) Report(m *APIMetric) { 35 select { 36 case metricCh <- m: 37 default: // unblocking 38 } 39 } 40 41 type APIStat struct { 42 Total int 43 LatencyMax, 44 LatencyAvg, 45 Latency time.Duration 46 47 Status1XX, 48 Status2XX, 49 Status3XX, 50 Status4XX, 51 Status5XX int 52 53 Limited int 54 } 55 56 type qAPIStats struct { 57 stats chan map[string]*APIStat 58 } 59 60 func GetStats() map[string]*APIStat { 61 q := &qAPIStats{stats: make(chan map[string]*APIStat)} 62 63 defer close(q.stats) 64 65 tick := time.NewTicker(time.Second * 1) 66 defer tick.Stop() 67 qch <- q 68 select { 69 case r := <-q.stats: 70 return r 71 case <-tick.C: 72 return nil 73 } 74 } 75 76 func copyStats(s map[string]*APIStat) map[string]*APIStat { 77 res := map[string]*APIStat{} 78 for k, v := range s { 79 res[k] = &APIStat{ 80 Total: v.Total, 81 LatencyMax: v.LatencyMax, 82 LatencyAvg: v.LatencyAvg, 83 Latency: v.Latency, 84 Status1XX: v.Status1XX, 85 Status2XX: v.Status2XX, 86 Status3XX: v.Status3XX, 87 Status4XX: v.Status4XX, 88 Status5XX: v.Status5XX, 89 Limited: v.Limited, 90 } 91 } 92 return res 93 } 94 95 func StopReporter() { 96 select { 97 case <-exitch: // closed? 98 return 99 default: 100 close(exitch) 101 } 102 } 103 104 func StartReporter() { 105 stats := map[string]*APIStat{} 106 for { 107 select { 108 case <-exitch: 109 return 110 111 case q := <-qch: 112 select { 113 case <-q.stats: // is closed? 114 case q.stats <- copyStats(stats): 115 default: // unblocking 116 } 117 118 case m := <-metricCh: 119 v, ok := stats[m.API] 120 if !ok { 121 v = &APIStat{} 122 stats[m.API] = v 123 } 124 125 v.Total++ 126 v.Latency += m.Latency 127 v.LatencyAvg = v.Latency / time.Duration(v.Total) 128 if m.Limited { 129 v.Limited++ 130 } 131 if m.Latency > v.LatencyMax { 132 v.LatencyMax = m.Latency 133 } 134 switch m.StatusCode / 100 { 135 case 1: 136 v.Status1XX++ 137 case 2: 138 v.Status2XX++ 139 case 3: 140 v.Status3XX++ 141 case 4: 142 v.Status4XX++ 143 case 5: 144 v.Status5XX++ 145 } 146 } 147 } 148 }