1// Copyright 2018, OpenCensus Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package metric 16 17import ( 18 "sort" 19 "sync" 20 "time" 21 22 "go.opencensus.io/metric/metricdata" 23) 24 25// Registry creates and manages a set of gauges and cumulative. 26// External synchronization is required if you want to add gauges and cumulative to the same 27// registry from multiple goroutines. 28type Registry struct { 29 baseMetrics sync.Map 30} 31 32type metricOptions struct { 33 unit metricdata.Unit 34 labelkeys []metricdata.LabelKey 35 constLabels map[metricdata.LabelKey]metricdata.LabelValue 36 desc string 37} 38 39// Options apply changes to metricOptions. 40type Options func(*metricOptions) 41 42// WithDescription applies provided description. 43func WithDescription(desc string) Options { 44 return func(mo *metricOptions) { 45 mo.desc = desc 46 } 47} 48 49// WithUnit applies provided unit. 50func WithUnit(unit metricdata.Unit) Options { 51 return func(mo *metricOptions) { 52 mo.unit = unit 53 } 54} 55 56// WithLabelKeys applies provided label. 57func WithLabelKeys(keys ...string) Options { 58 return func(mo *metricOptions) { 59 labelKeys := make([]metricdata.LabelKey, 0) 60 for _, key := range keys { 61 labelKeys = append(labelKeys, metricdata.LabelKey{Key: key}) 62 } 63 mo.labelkeys = labelKeys 64 } 65} 66 67// WithLabelKeysAndDescription applies provided label. 68func WithLabelKeysAndDescription(labelKeys ...metricdata.LabelKey) Options { 69 return func(mo *metricOptions) { 70 mo.labelkeys = labelKeys 71 } 72} 73 74// WithConstLabel applies provided constant label. 75func WithConstLabel(constLabels map[metricdata.LabelKey]metricdata.LabelValue) Options { 76 return func(mo *metricOptions) { 77 mo.constLabels = constLabels 78 } 79} 80 81// NewRegistry initializes a new Registry. 82func NewRegistry() *Registry { 83 return &Registry{} 84} 85 86// AddFloat64Gauge creates and adds a new float64-valued gauge to this registry. 87func (r *Registry) AddFloat64Gauge(name string, mos ...Options) (*Float64Gauge, error) { 88 f := &Float64Gauge{ 89 bm: baseMetric{ 90 bmType: gaugeFloat64, 91 }, 92 } 93 _, err := r.initBaseMetric(&f.bm, name, mos...) 94 if err != nil { 95 return nil, err 96 } 97 return f, nil 98} 99 100// AddInt64Gauge creates and adds a new int64-valued gauge to this registry. 101func (r *Registry) AddInt64Gauge(name string, mos ...Options) (*Int64Gauge, error) { 102 i := &Int64Gauge{ 103 bm: baseMetric{ 104 bmType: gaugeInt64, 105 }, 106 } 107 _, err := r.initBaseMetric(&i.bm, name, mos...) 108 if err != nil { 109 return nil, err 110 } 111 return i, nil 112} 113 114// AddInt64DerivedGauge creates and adds a new derived int64-valued gauge to this registry. 115// A derived gauge is convenient form of gauge where the object associated with the gauge 116// provides its value by implementing func() int64. 117func (r *Registry) AddInt64DerivedGauge(name string, mos ...Options) (*Int64DerivedGauge, error) { 118 i := &Int64DerivedGauge{ 119 bm: baseMetric{ 120 bmType: derivedGaugeInt64, 121 }, 122 } 123 _, err := r.initBaseMetric(&i.bm, name, mos...) 124 if err != nil { 125 return nil, err 126 } 127 return i, nil 128} 129 130// AddFloat64DerivedGauge creates and adds a new derived float64-valued gauge to this registry. 131// A derived gauge is convenient form of gauge where the object associated with the gauge 132// provides its value by implementing func() float64. 133func (r *Registry) AddFloat64DerivedGauge(name string, mos ...Options) (*Float64DerivedGauge, error) { 134 f := &Float64DerivedGauge{ 135 bm: baseMetric{ 136 bmType: derivedGaugeFloat64, 137 }, 138 } 139 _, err := r.initBaseMetric(&f.bm, name, mos...) 140 if err != nil { 141 return nil, err 142 } 143 return f, nil 144} 145 146func bmTypeToMetricType(bm *baseMetric) metricdata.Type { 147 switch bm.bmType { 148 case derivedGaugeFloat64: 149 return metricdata.TypeGaugeFloat64 150 case derivedGaugeInt64: 151 return metricdata.TypeGaugeInt64 152 case gaugeFloat64: 153 return metricdata.TypeGaugeFloat64 154 case gaugeInt64: 155 return metricdata.TypeGaugeInt64 156 case derivedCumulativeFloat64: 157 return metricdata.TypeCumulativeFloat64 158 case derivedCumulativeInt64: 159 return metricdata.TypeCumulativeInt64 160 case cumulativeFloat64: 161 return metricdata.TypeCumulativeFloat64 162 case cumulativeInt64: 163 return metricdata.TypeCumulativeInt64 164 default: 165 panic("unsupported metric type") 166 } 167} 168 169// AddFloat64Cumulative creates and adds a new float64-valued cumulative to this registry. 170func (r *Registry) AddFloat64Cumulative(name string, mos ...Options) (*Float64Cumulative, error) { 171 f := &Float64Cumulative{ 172 bm: baseMetric{ 173 bmType: cumulativeFloat64, 174 }, 175 } 176 _, err := r.initBaseMetric(&f.bm, name, mos...) 177 if err != nil { 178 return nil, err 179 } 180 return f, nil 181} 182 183// AddInt64Cumulative creates and adds a new int64-valued cumulative to this registry. 184func (r *Registry) AddInt64Cumulative(name string, mos ...Options) (*Int64Cumulative, error) { 185 i := &Int64Cumulative{ 186 bm: baseMetric{ 187 bmType: cumulativeInt64, 188 }, 189 } 190 _, err := r.initBaseMetric(&i.bm, name, mos...) 191 if err != nil { 192 return nil, err 193 } 194 return i, nil 195} 196 197// AddInt64DerivedCumulative creates and adds a new derived int64-valued cumulative to this registry. 198// A derived cumulative is convenient form of cumulative where the object associated with the cumulative 199// provides its value by implementing func() int64. 200func (r *Registry) AddInt64DerivedCumulative(name string, mos ...Options) (*Int64DerivedCumulative, error) { 201 i := &Int64DerivedCumulative{ 202 bm: baseMetric{ 203 bmType: derivedCumulativeInt64, 204 }, 205 } 206 _, err := r.initBaseMetric(&i.bm, name, mos...) 207 if err != nil { 208 return nil, err 209 } 210 return i, nil 211} 212 213// AddFloat64DerivedCumulative creates and adds a new derived float64-valued gauge to this registry. 214// A derived cumulative is convenient form of cumulative where the object associated with the cumulative 215// provides its value by implementing func() float64. 216func (r *Registry) AddFloat64DerivedCumulative(name string, mos ...Options) (*Float64DerivedCumulative, error) { 217 f := &Float64DerivedCumulative{ 218 bm: baseMetric{ 219 bmType: derivedCumulativeFloat64, 220 }, 221 } 222 _, err := r.initBaseMetric(&f.bm, name, mos...) 223 if err != nil { 224 return nil, err 225 } 226 return f, nil 227} 228 229func createMetricOption(mos ...Options) *metricOptions { 230 o := &metricOptions{} 231 for _, mo := range mos { 232 mo(o) 233 } 234 return o 235} 236 237func (r *Registry) initBaseMetric(bm *baseMetric, name string, mos ...Options) (*baseMetric, error) { 238 val, ok := r.baseMetrics.Load(name) 239 if ok { 240 existing := val.(*baseMetric) 241 if existing.bmType != bm.bmType { 242 return nil, errMetricExistsWithDiffType 243 } 244 } 245 bm.start = time.Now() 246 o := createMetricOption(mos...) 247 248 var constLabelKeys []metricdata.LabelKey 249 for k := range o.constLabels { 250 constLabelKeys = append(constLabelKeys, k) 251 } 252 sort.Slice(constLabelKeys, func(i, j int) bool { 253 return constLabelKeys[i].Key < constLabelKeys[j].Key 254 }) 255 256 var constLabelValues []metricdata.LabelValue 257 for _, k := range constLabelKeys { 258 constLabelValues = append(constLabelValues, o.constLabels[k]) 259 } 260 261 bm.keys = append(constLabelKeys, o.labelkeys...) 262 bm.constLabelValues = constLabelValues 263 264 bm.desc = metricdata.Descriptor{ 265 Name: name, 266 Description: o.desc, 267 Unit: o.unit, 268 LabelKeys: bm.keys, 269 Type: bmTypeToMetricType(bm), 270 } 271 r.baseMetrics.Store(name, bm) 272 return bm, nil 273} 274 275// Read reads all gauges and cumulatives in this registry and returns their values as metrics. 276func (r *Registry) Read() []*metricdata.Metric { 277 ms := []*metricdata.Metric{} 278 r.baseMetrics.Range(func(k, v interface{}) bool { 279 bm := v.(*baseMetric) 280 ms = append(ms, bm.read()) 281 return true 282 }) 283 return ms 284} 285