1 /*
2 *
3 * Copyright 2024 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * 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 19 package stats
20 21 import (
22 "maps"
23 24 "google.golang.org/grpc/grpclog"
25 "google.golang.org/grpc/internal"
26 "google.golang.org/grpc/stats"
27 )
28 29 func init() {
30 internal.SnapshotMetricRegistryForTesting = snapshotMetricsRegistryForTesting
31 }
32 33 var logger = grpclog.Component("metrics-registry")
34 35 // DefaultMetrics are the default metrics registered through global metrics
36 // registry. This is written to at initialization time only, and is read only
37 // after initialization.
38 var DefaultMetrics = stats.NewMetricSet()
39 40 // MetricDescriptor is the data for a registered metric.
41 type MetricDescriptor struct {
42 // The name of this metric. This name must be unique across the whole binary
43 // (including any per call metrics). See
44 // https://github.com/grpc/proposal/blob/master/A79-non-per-call-metrics-architecture.md#metric-instrument-naming-conventions
45 // for metric naming conventions.
46 Name string
47 // The description of this metric.
48 Description string
49 // The unit (e.g. entries, seconds) of this metric.
50 Unit string
51 // The required label keys for this metric. These are intended to
52 // metrics emitted from a stats handler.
53 Labels []string
54 // The optional label keys for this metric. These are intended to attached
55 // to metrics emitted from a stats handler if configured.
56 OptionalLabels []string
57 // Whether this metric is on by default.
58 Default bool
59 // The type of metric. This is set by the metric registry, and not intended
60 // to be set by a component registering a metric.
61 Type MetricType
62 // Bounds are the bounds of this metric. This only applies to histogram
63 // metrics. If unset or set with length 0, stats handlers will fall back to
64 // default bounds.
65 Bounds []float64
66 }
67 68 // MetricType is the type of metric.
69 type MetricType int
70 71 // Type of metric supported by this instrument registry.
72 const (
73 MetricTypeIntCount MetricType = iota
74 MetricTypeFloatCount
75 MetricTypeIntHisto
76 MetricTypeFloatHisto
77 MetricTypeIntGauge
78 MetricTypeIntUpDownCount
79 MetricTypeIntAsyncGauge
80 )
81 82 // Int64CountHandle is a typed handle for a int count metric. This handle
83 // is passed at the recording point in order to know which metric to record
84 // on.
85 type Int64CountHandle MetricDescriptor
86 87 // Descriptor returns the int64 count handle typecast to a pointer to a
88 // MetricDescriptor.
89 func (h *Int64CountHandle) Descriptor() *MetricDescriptor {
90 return (*MetricDescriptor)(h)
91 }
92 93 // Record records the int64 count value on the metrics recorder provided.
94 func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
95 recorder.RecordInt64Count(h, incr, labels...)
96 }
97 98 // Int64UpDownCountHandle is a typed handle for an int up-down counter metric.
99 // This handle is passed at the recording point in order to know which metric
100 // to record on.
101 type Int64UpDownCountHandle MetricDescriptor
102 103 // Descriptor returns the int64 up-down counter handle typecast to a pointer to a
104 // MetricDescriptor.
105 func (h *Int64UpDownCountHandle) Descriptor() *MetricDescriptor {
106 return (*MetricDescriptor)(h)
107 }
108 109 // Record records the int64 up-down counter value on the metrics recorder provided.
110 // The value 'v' can be positive to increment or negative to decrement.
111 func (h *Int64UpDownCountHandle) Record(recorder MetricsRecorder, v int64, labels ...string) {
112 recorder.RecordInt64UpDownCount(h, v, labels...)
113 }
114 115 // Float64CountHandle is a typed handle for a float count metric. This handle is
116 // passed at the recording point in order to know which metric to record on.
117 type Float64CountHandle MetricDescriptor
118 119 // Descriptor returns the float64 count handle typecast to a pointer to a
120 // MetricDescriptor.
121 func (h *Float64CountHandle) Descriptor() *MetricDescriptor {
122 return (*MetricDescriptor)(h)
123 }
124 125 // Record records the float64 count value on the metrics recorder provided.
126 func (h *Float64CountHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
127 recorder.RecordFloat64Count(h, incr, labels...)
128 }
129 130 // Int64HistoHandle is a typed handle for an int histogram metric. This handle
131 // is passed at the recording point in order to know which metric to record on.
132 type Int64HistoHandle MetricDescriptor
133 134 // Descriptor returns the int64 histo handle typecast to a pointer to a
135 // MetricDescriptor.
136 func (h *Int64HistoHandle) Descriptor() *MetricDescriptor {
137 return (*MetricDescriptor)(h)
138 }
139 140 // Record records the int64 histo value on the metrics recorder provided.
141 func (h *Int64HistoHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
142 recorder.RecordInt64Histo(h, incr, labels...)
143 }
144 145 // Float64HistoHandle is a typed handle for a float histogram metric. This
146 // handle is passed at the recording point in order to know which metric to
147 // record on.
148 type Float64HistoHandle MetricDescriptor
149 150 // Descriptor returns the float64 histo handle typecast to a pointer to a
151 // MetricDescriptor.
152 func (h *Float64HistoHandle) Descriptor() *MetricDescriptor {
153 return (*MetricDescriptor)(h)
154 }
155 156 // Record records the float64 histo value on the metrics recorder provided.
157 func (h *Float64HistoHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
158 recorder.RecordFloat64Histo(h, incr, labels...)
159 }
160 161 // Int64GaugeHandle is a typed handle for an int gauge metric. This handle is
162 // passed at the recording point in order to know which metric to record on.
163 type Int64GaugeHandle MetricDescriptor
164 165 // Descriptor returns the int64 gauge handle typecast to a pointer to a
166 // MetricDescriptor.
167 func (h *Int64GaugeHandle) Descriptor() *MetricDescriptor {
168 return (*MetricDescriptor)(h)
169 }
170 171 // Record records the int64 histo value on the metrics recorder provided.
172 func (h *Int64GaugeHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
173 recorder.RecordInt64Gauge(h, incr, labels...)
174 }
175 176 // AsyncMetric is a marker interface for asynchronous metric types.
177 type AsyncMetric interface {
178 isAsync()
179 Descriptor() *MetricDescriptor
180 }
181 182 // Int64AsyncGaugeHandle is a typed handle for an int gauge metric. This handle is
183 // passed at the recording point in order to know which metric to record on.
184 type Int64AsyncGaugeHandle MetricDescriptor
185 186 // isAsync implements the AsyncMetric interface.
187 func (h *Int64AsyncGaugeHandle) isAsync() {}
188 189 // Descriptor returns the int64 gauge handle typecast to a pointer to a
190 // MetricDescriptor.
191 func (h *Int64AsyncGaugeHandle) Descriptor() *MetricDescriptor {
192 return (*MetricDescriptor)(h)
193 }
194 195 // Record records the int64 gauge value on the metrics recorder provided.
196 func (h *Int64AsyncGaugeHandle) Record(recorder AsyncMetricsRecorder, value int64, labels ...string) {
197 recorder.RecordInt64AsyncGauge(h, value, labels...)
198 }
199 200 // registeredMetrics are the registered metric descriptor names.
201 var registeredMetrics = make(map[string]bool)
202 203 // metricsRegistry contains all of the registered metrics.
204 //
205 // This is written to only at init time, and read only after that.
206 var metricsRegistry = make(map[string]*MetricDescriptor)
207 208 // DescriptorForMetric returns the MetricDescriptor from the global registry.
209 //
210 // Returns nil if MetricDescriptor not present.
211 func DescriptorForMetric(metricName string) *MetricDescriptor {
212 return metricsRegistry[metricName]
213 }
214 215 func registerMetric(metricName string, def bool) {
216 if registeredMetrics[metricName] {
217 logger.Fatalf("metric %v already registered", metricName)
218 }
219 registeredMetrics[metricName] = true
220 if def {
221 DefaultMetrics = DefaultMetrics.Add(metricName)
222 }
223 }
224 225 // RegisterInt64Count registers the metric description onto the global registry.
226 // It returns a typed handle to use to recording data.
227 //
228 // NOTE: this function must only be called during initialization time (i.e. in
229 // an init() function), and is not thread-safe. If multiple metrics are
230 // registered with the same name, this function will panic.
231 func RegisterInt64Count(descriptor MetricDescriptor) *Int64CountHandle {
232 registerMetric(descriptor.Name, descriptor.Default)
233 descriptor.Type = MetricTypeIntCount
234 descPtr := &descriptor
235 metricsRegistry[descriptor.Name] = descPtr
236 return (*Int64CountHandle)(descPtr)
237 }
238 239 // RegisterFloat64Count registers the metric description onto the global
240 // registry. It returns a typed handle to use to recording data.
241 //
242 // NOTE: this function must only be called during initialization time (i.e. in
243 // an init() function), and is not thread-safe. If multiple metrics are
244 // registered with the same name, this function will panic.
245 func RegisterFloat64Count(descriptor MetricDescriptor) *Float64CountHandle {
246 registerMetric(descriptor.Name, descriptor.Default)
247 descriptor.Type = MetricTypeFloatCount
248 descPtr := &descriptor
249 metricsRegistry[descriptor.Name] = descPtr
250 return (*Float64CountHandle)(descPtr)
251 }
252 253 // RegisterInt64Histo registers the metric description onto the global registry.
254 // It returns a typed handle to use to recording data.
255 //
256 // NOTE: this function must only be called during initialization time (i.e. in
257 // an init() function), and is not thread-safe. If multiple metrics are
258 // registered with the same name, this function will panic.
259 func RegisterInt64Histo(descriptor MetricDescriptor) *Int64HistoHandle {
260 registerMetric(descriptor.Name, descriptor.Default)
261 descriptor.Type = MetricTypeIntHisto
262 descPtr := &descriptor
263 metricsRegistry[descriptor.Name] = descPtr
264 return (*Int64HistoHandle)(descPtr)
265 }
266 267 // RegisterFloat64Histo registers the metric description onto the global
268 // registry. It returns a typed handle to use to recording data.
269 //
270 // NOTE: this function must only be called during initialization time (i.e. in
271 // an init() function), and is not thread-safe. If multiple metrics are
272 // registered with the same name, this function will panic.
273 func RegisterFloat64Histo(descriptor MetricDescriptor) *Float64HistoHandle {
274 registerMetric(descriptor.Name, descriptor.Default)
275 descriptor.Type = MetricTypeFloatHisto
276 descPtr := &descriptor
277 metricsRegistry[descriptor.Name] = descPtr
278 return (*Float64HistoHandle)(descPtr)
279 }
280 281 // RegisterInt64Gauge registers the metric description onto the global registry.
282 // It returns a typed handle to use to recording data.
283 //
284 // NOTE: this function must only be called during initialization time (i.e. in
285 // an init() function), and is not thread-safe. If multiple metrics are
286 // registered with the same name, this function will panic.
287 func RegisterInt64Gauge(descriptor MetricDescriptor) *Int64GaugeHandle {
288 registerMetric(descriptor.Name, descriptor.Default)
289 descriptor.Type = MetricTypeIntGauge
290 descPtr := &descriptor
291 metricsRegistry[descriptor.Name] = descPtr
292 return (*Int64GaugeHandle)(descPtr)
293 }
294 295 // RegisterInt64UpDownCount registers the metric description onto the global registry.
296 // It returns a typed handle to use for recording data.
297 //
298 // NOTE: this function must only be called during initialization time (i.e. in
299 // an init() function), and is not thread-safe. If multiple metrics are
300 // registered with the same name, this function will panic.
301 func RegisterInt64UpDownCount(descriptor MetricDescriptor) *Int64UpDownCountHandle {
302 registerMetric(descriptor.Name, descriptor.Default)
303 // Set the specific metric type for the up-down counter
304 descriptor.Type = MetricTypeIntUpDownCount
305 descPtr := &descriptor
306 metricsRegistry[descriptor.Name] = descPtr
307 return (*Int64UpDownCountHandle)(descPtr)
308 }
309 310 // RegisterInt64AsyncGauge registers the metric description onto the global registry.
311 // It returns a typed handle to use for recording data.
312 //
313 // NOTE: this function must only be called during initialization time (i.e. in
314 // an init() function), and is not thread-safe. If multiple metrics are
315 // registered with the same name, this function will panic.
316 func RegisterInt64AsyncGauge(descriptor MetricDescriptor) *Int64AsyncGaugeHandle {
317 registerMetric(descriptor.Name, descriptor.Default)
318 descriptor.Type = MetricTypeIntAsyncGauge
319 descPtr := &descriptor
320 metricsRegistry[descriptor.Name] = descPtr
321 return (*Int64AsyncGaugeHandle)(descPtr)
322 }
323 324 // snapshotMetricsRegistryForTesting snapshots the global data of the metrics
325 // registry. Returns a cleanup function that sets the metrics registry to its
326 // original state.
327 func snapshotMetricsRegistryForTesting() func() {
328 oldDefaultMetrics := DefaultMetrics
329 oldRegisteredMetrics := registeredMetrics
330 oldMetricsRegistry := metricsRegistry
331 332 registeredMetrics = make(map[string]bool)
333 metricsRegistry = make(map[string]*MetricDescriptor)
334 maps.Copy(registeredMetrics, registeredMetrics)
335 maps.Copy(metricsRegistry, metricsRegistry)
336 337 return func() {
338 DefaultMetrics = oldDefaultMetrics
339 registeredMetrics = oldRegisteredMetrics
340 metricsRegistry = oldMetricsRegistry
341 }
342 }
343