dubbo.apache.org/dubbo-go/v3@v3.1.1/metrics/prometheus/rt_vec.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package prometheus 19 20 import ( 21 "bytes" 22 "sync" 23 ) 24 25 import ( 26 prom "github.com/prometheus/client_golang/prometheus" 27 ) 28 29 import ( 30 "dubbo.apache.org/dubbo-go/v3/metrics/util/aggregate" 31 ) 32 33 type rtMetric struct { 34 nameSuffix string 35 helpPrefix string 36 valueFunc func(*aggregate.Result) float64 37 } 38 39 func (m *rtMetric) desc(opts *RtOpts, labels []string) *prom.Desc { 40 return prom.NewDesc(prom.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name+m.nameSuffix), m.helpPrefix+opts.Help, labels, opts.ConstLabels) 41 } 42 43 var ( 44 rtMetrics = make([]*rtMetric, 5) 45 aggMetrics = make([]*rtMetric, 3) 46 ) 47 48 func init() { 49 rtMetrics[0] = &rtMetric{nameSuffix: "_sum", helpPrefix: "Sum ", valueFunc: func(r *aggregate.Result) float64 { return r.Total }} 50 rtMetrics[1] = &rtMetric{nameSuffix: "_last", helpPrefix: "Last ", valueFunc: func(r *aggregate.Result) float64 { return r.Last }} 51 rtMetrics[2] = &rtMetric{nameSuffix: "_min", helpPrefix: "Min ", valueFunc: func(r *aggregate.Result) float64 { return r.Min }} 52 rtMetrics[3] = &rtMetric{nameSuffix: "_max", helpPrefix: "Max ", valueFunc: func(r *aggregate.Result) float64 { return r.Max }} 53 rtMetrics[4] = &rtMetric{nameSuffix: "_avg", helpPrefix: "Average ", valueFunc: func(r *aggregate.Result) float64 { return r.Avg }} 54 55 aggMetrics[0] = &rtMetric{nameSuffix: "_avg_milliseconds_aggregate", helpPrefix: "The average ", valueFunc: func(r *aggregate.Result) float64 { return r.Avg }} 56 aggMetrics[1] = &rtMetric{nameSuffix: "_min_milliseconds_aggregate", helpPrefix: "The minimum ", valueFunc: func(r *aggregate.Result) float64 { return r.Min }} 57 aggMetrics[2] = &rtMetric{nameSuffix: "_max_milliseconds_aggregate", helpPrefix: "The maximum ", valueFunc: func(r *aggregate.Result) float64 { return r.Max }} 58 } 59 60 type RtOpts struct { 61 Namespace string 62 Subsystem string 63 Name string 64 Help string 65 ConstLabels prom.Labels 66 bucketNum int // only for aggRt 67 timeWindowSeconds int64 // only for aggRt 68 } 69 70 type observer interface { 71 Observe(val float64) 72 result() *aggregate.Result 73 } 74 75 type aggResult struct { 76 agg *aggregate.TimeWindowAggregator 77 } 78 79 func (r *aggResult) Observe(val float64) { 80 r.agg.Add(val) 81 } 82 83 func (r *aggResult) result() *aggregate.Result { 84 return r.agg.Result() 85 } 86 87 type valueResult struct { 88 mtx sync.RWMutex 89 val *aggregate.Result 90 } 91 92 func (r *valueResult) Observe(val float64) { 93 r.mtx.Lock() 94 defer r.mtx.Unlock() 95 r.val.Update(val) 96 } 97 98 func (r *valueResult) result() *aggregate.Result { 99 res := aggregate.NewResult() 100 r.mtx.RLock() 101 res.Merge(r.val) 102 r.mtx.RUnlock() 103 return res.Get() 104 } 105 106 type Rt struct { 107 tags map[string]string 108 obs observer 109 } 110 111 func (r *Rt) Observe(val float64) { 112 r.obs.Observe(val) 113 } 114 115 func buildKey(m map[string]string, labNames []string) string { 116 var buffer bytes.Buffer 117 for _, label := range labNames { 118 if buffer.Len() != 0 { 119 buffer.WriteString("_") 120 } 121 buffer.WriteString(m[label]) 122 } 123 return buffer.String() 124 } 125 126 func buildLabelValues(m map[string]string, labNames []string) []string { 127 values := make([]string, len(labNames)) 128 for i, label := range labNames { 129 values[i] = m[label] 130 } 131 return values 132 } 133 134 type RtVec struct { 135 opts *RtOpts 136 labelNames []string 137 aggMap sync.Map 138 initFunc func(tags map[string]string) *Rt 139 metrics []*rtMetric 140 } 141 142 func NewRtVec(opts *RtOpts, labNames []string) *RtVec { 143 return &RtVec{ 144 opts: opts, 145 labelNames: labNames, 146 metrics: rtMetrics, 147 initFunc: func(tags map[string]string) *Rt { 148 return &Rt{ 149 tags: tags, 150 obs: &valueResult{val: aggregate.NewResult()}, 151 } 152 }, 153 } 154 } 155 156 func NewAggRtVec(opts *RtOpts, labNames []string) *RtVec { 157 return &RtVec{ 158 opts: opts, 159 labelNames: labNames, 160 metrics: aggMetrics, 161 initFunc: func(tags map[string]string) *Rt { 162 return &Rt{ 163 tags: tags, 164 obs: &aggResult{agg: aggregate.NewTimeWindowAggregator(opts.bucketNum, opts.timeWindowSeconds)}, 165 } 166 }, 167 } 168 } 169 170 func (r *RtVec) With(tags map[string]string) prom.Observer { 171 k := buildKey(tags, r.labelNames) 172 return r.computeIfAbsent(k, func() *Rt { 173 return r.initFunc(tags) 174 }) 175 } 176 177 func (r *RtVec) computeIfAbsent(k string, supplier func() *Rt) *Rt { 178 v, ok := r.aggMap.Load(k) 179 if !ok { 180 v, _ = r.aggMap.LoadOrStore(k, supplier()) 181 } 182 return v.(*Rt) 183 } 184 185 func (r *RtVec) Collect(ch chan<- prom.Metric) { 186 r.aggMap.Range(func(_, val interface{}) bool { 187 v := val.(*Rt) 188 res := v.obs.result() 189 for _, m := range r.metrics { 190 ch <- prom.MustNewConstMetric(m.desc(r.opts, r.labelNames), prom.GaugeValue, m.valueFunc(res), buildLabelValues(v.tags, r.labelNames)...) 191 } 192 return true 193 }) 194 } 195 196 func (r *RtVec) Describe(ch chan<- *prom.Desc) { 197 for _, m := range r.metrics { 198 ch <- m.desc(r.opts, r.labelNames) 199 } 200 }