metricregistry.go

  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}