metricregistry.go raw

   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