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
19package stats
20
21import (
22 "maps"
23
24 "google.golang.org/grpc/grpclog"
25 "google.golang.org/grpc/internal"
26 "google.golang.org/grpc/stats"
27)
28
29func init() {
30 internal.SnapshotMetricRegistryForTesting = snapshotMetricsRegistryForTesting
31}
32
33var 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.
38var DefaultMetrics = stats.NewMetricSet()
39
40// MetricDescriptor is the data for a registered metric.
41type 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.
69type MetricType int
70
71// Type of metric supported by this instrument registry.
72const (
73 MetricTypeIntCount MetricType = iota
74 MetricTypeFloatCount
75 MetricTypeIntHisto
76 MetricTypeFloatHisto
77 MetricTypeIntGauge
78)
79
80// Int64CountHandle is a typed handle for a int count metric. This handle
81// is passed at the recording point in order to know which metric to record
82// on.
83type Int64CountHandle MetricDescriptor
84
85// Descriptor returns the int64 count handle typecast to a pointer to a
86// MetricDescriptor.
87func (h *Int64CountHandle) Descriptor() *MetricDescriptor {
88 return (*MetricDescriptor)(h)
89}
90
91// Record records the int64 count value on the metrics recorder provided.
92func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
93 recorder.RecordInt64Count(h, incr, labels...)
94}
95
96// Float64CountHandle is a typed handle for a float count metric. This handle is
97// passed at the recording point in order to know which metric to record on.
98type Float64CountHandle MetricDescriptor
99
100// Descriptor returns the float64 count handle typecast to a pointer to a
101// MetricDescriptor.
102func (h *Float64CountHandle) Descriptor() *MetricDescriptor {
103 return (*MetricDescriptor)(h)
104}
105
106// Record records the float64 count value on the metrics recorder provided.
107func (h *Float64CountHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
108 recorder.RecordFloat64Count(h, incr, labels...)
109}
110
111// Int64HistoHandle is a typed handle for an int histogram metric. This handle
112// is passed at the recording point in order to know which metric to record on.
113type Int64HistoHandle MetricDescriptor
114
115// Descriptor returns the int64 histo handle typecast to a pointer to a
116// MetricDescriptor.
117func (h *Int64HistoHandle) Descriptor() *MetricDescriptor {
118 return (*MetricDescriptor)(h)
119}
120
121// Record records the int64 histo value on the metrics recorder provided.
122func (h *Int64HistoHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
123 recorder.RecordInt64Histo(h, incr, labels...)
124}
125
126// Float64HistoHandle is a typed handle for a float histogram metric. This
127// handle is passed at the recording point in order to know which metric to
128// record on.
129type Float64HistoHandle MetricDescriptor
130
131// Descriptor returns the float64 histo handle typecast to a pointer to a
132// MetricDescriptor.
133func (h *Float64HistoHandle) Descriptor() *MetricDescriptor {
134 return (*MetricDescriptor)(h)
135}
136
137// Record records the float64 histo value on the metrics recorder provided.
138func (h *Float64HistoHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
139 recorder.RecordFloat64Histo(h, incr, labels...)
140}
141
142// Int64GaugeHandle is a typed handle for an int gauge metric. This handle is
143// passed at the recording point in order to know which metric to record on.
144type Int64GaugeHandle MetricDescriptor
145
146// Descriptor returns the int64 gauge handle typecast to a pointer to a
147// MetricDescriptor.
148func (h *Int64GaugeHandle) Descriptor() *MetricDescriptor {
149 return (*MetricDescriptor)(h)
150}
151
152// Record records the int64 histo value on the metrics recorder provided.
153func (h *Int64GaugeHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
154 recorder.RecordInt64Gauge(h, incr, labels...)
155}
156
157// registeredMetrics are the registered metric descriptor names.
158var registeredMetrics = make(map[string]bool)
159
160// metricsRegistry contains all of the registered metrics.
161//
162// This is written to only at init time, and read only after that.
163var metricsRegistry = make(map[string]*MetricDescriptor)
164
165// DescriptorForMetric returns the MetricDescriptor from the global registry.
166//
167// Returns nil if MetricDescriptor not present.
168func DescriptorForMetric(metricName string) *MetricDescriptor {
169 return metricsRegistry[metricName]
170}
171
172func registerMetric(metricName string, def bool) {
173 if registeredMetrics[metricName] {
174 logger.Fatalf("metric %v already registered", metricName)
175 }
176 registeredMetrics[metricName] = true
177 if def {
178 DefaultMetrics = DefaultMetrics.Add(metricName)
179 }
180}
181
182// RegisterInt64Count registers the metric description onto the global registry.
183// It returns a typed handle to use to recording data.
184//
185// NOTE: this function must only be called during initialization time (i.e. in
186// an init() function), and is not thread-safe. If multiple metrics are
187// registered with the same name, this function will panic.
188func RegisterInt64Count(descriptor MetricDescriptor) *Int64CountHandle {
189 registerMetric(descriptor.Name, descriptor.Default)
190 descriptor.Type = MetricTypeIntCount
191 descPtr := &descriptor
192 metricsRegistry[descriptor.Name] = descPtr
193 return (*Int64CountHandle)(descPtr)
194}
195
196// RegisterFloat64Count registers the metric description onto the global
197// registry. It returns a typed handle to use to recording data.
198//
199// NOTE: this function must only be called during initialization time (i.e. in
200// an init() function), and is not thread-safe. If multiple metrics are
201// registered with the same name, this function will panic.
202func RegisterFloat64Count(descriptor MetricDescriptor) *Float64CountHandle {
203 registerMetric(descriptor.Name, descriptor.Default)
204 descriptor.Type = MetricTypeFloatCount
205 descPtr := &descriptor
206 metricsRegistry[descriptor.Name] = descPtr
207 return (*Float64CountHandle)(descPtr)
208}
209
210// RegisterInt64Histo registers the metric description onto the global registry.
211// It returns a typed handle to use to recording data.
212//
213// NOTE: this function must only be called during initialization time (i.e. in
214// an init() function), and is not thread-safe. If multiple metrics are
215// registered with the same name, this function will panic.
216func RegisterInt64Histo(descriptor MetricDescriptor) *Int64HistoHandle {
217 registerMetric(descriptor.Name, descriptor.Default)
218 descriptor.Type = MetricTypeIntHisto
219 descPtr := &descriptor
220 metricsRegistry[descriptor.Name] = descPtr
221 return (*Int64HistoHandle)(descPtr)
222}
223
224// RegisterFloat64Histo registers the metric description onto the global
225// registry. It returns a typed handle to use to recording data.
226//
227// NOTE: this function must only be called during initialization time (i.e. in
228// an init() function), and is not thread-safe. If multiple metrics are
229// registered with the same name, this function will panic.
230func RegisterFloat64Histo(descriptor MetricDescriptor) *Float64HistoHandle {
231 registerMetric(descriptor.Name, descriptor.Default)
232 descriptor.Type = MetricTypeFloatHisto
233 descPtr := &descriptor
234 metricsRegistry[descriptor.Name] = descPtr
235 return (*Float64HistoHandle)(descPtr)
236}
237
238// RegisterInt64Gauge registers the metric description onto the global registry.
239// It returns a typed handle to use to recording data.
240//
241// NOTE: this function must only be called during initialization time (i.e. in
242// an init() function), and is not thread-safe. If multiple metrics are
243// registered with the same name, this function will panic.
244func RegisterInt64Gauge(descriptor MetricDescriptor) *Int64GaugeHandle {
245 registerMetric(descriptor.Name, descriptor.Default)
246 descriptor.Type = MetricTypeIntGauge
247 descPtr := &descriptor
248 metricsRegistry[descriptor.Name] = descPtr
249 return (*Int64GaugeHandle)(descPtr)
250}
251
252// snapshotMetricsRegistryForTesting snapshots the global data of the metrics
253// registry. Returns a cleanup function that sets the metrics registry to its
254// original state.
255func snapshotMetricsRegistryForTesting() func() {
256 oldDefaultMetrics := DefaultMetrics
257 oldRegisteredMetrics := registeredMetrics
258 oldMetricsRegistry := metricsRegistry
259
260 registeredMetrics = make(map[string]bool)
261 metricsRegistry = make(map[string]*MetricDescriptor)
262 maps.Copy(registeredMetrics, registeredMetrics)
263 maps.Copy(metricsRegistry, metricsRegistry)
264
265 return func() {
266 DefaultMetrics = oldDefaultMetrics
267 registeredMetrics = oldRegisteredMetrics
268 metricsRegistry = oldMetricsRegistry
269 }
270}