github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/experimental/metrics/implementation/prometheus/metrics.go (about)

     1  // Copyright 2022 Meta Platforms, Inc. and affiliates.
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
     4  //
     5  // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     6  //
     7  // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
     8  //
     9  // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    10  //
    11  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    12  
    13  // Copyright (c) Facebook, Inc. and its affiliates.
    14  //
    15  // This source code is licensed under the MIT license found in the
    16  // LICENSE file in the root directory of this source tree.
    17  
    18  package prometheus
    19  
    20  import (
    21  	"fmt"
    22  	"sort"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  
    27  	"github.com/facebookincubator/go-belt"
    28  	"github.com/facebookincubator/go-belt/pkg/field"
    29  	"github.com/facebookincubator/go-belt/tool/experimental/metrics"
    30  	"github.com/facebookincubator/go-belt/tool/experimental/metrics/types"
    31  	"github.com/go-ng/xsort"
    32  	"github.com/prometheus/client_golang/prometheus"
    33  )
    34  
    35  var _ types.Metrics = (*Metrics)(nil)
    36  
    37  // Fields is just a type-alias to field.Fields (for convenience).
    38  type Fields = field.Fields
    39  
    40  func mergeSortedStrings(dst []string, add ...string) []string {
    41  	if len(add) == 0 {
    42  		return dst
    43  	}
    44  	if len(dst) == 0 {
    45  		return append([]string{}, add...)
    46  	}
    47  
    48  	dstLen := len(dst)
    49  
    50  	if !sort.StringsAreSorted(dst) || !sort.StringsAreSorted(add) {
    51  		panic(fmt.Sprintf("%v %v", sort.StringsAreSorted(dst), sort.StringsAreSorted(add)))
    52  	}
    53  
    54  	i, j := 0, 0
    55  	for i < dstLen && j < len(add) {
    56  		switch strings.Compare(dst[i], add[j]) {
    57  		case -1:
    58  			i++
    59  		case 0:
    60  			i++
    61  			j++
    62  		case 1:
    63  			dst = append(dst, add[j])
    64  			j++
    65  		}
    66  	}
    67  	dst = append(dst, add[j:]...)
    68  	sort.Strings(dst)
    69  	return dst
    70  }
    71  
    72  func labelsWithPlaceholders(labels prometheus.Labels, placeholders []string) prometheus.Labels {
    73  	if len(labels) == len(placeholders) {
    74  		return labels
    75  	}
    76  	result := make(prometheus.Labels, len(placeholders))
    77  	for k, v := range labels {
    78  		result[k] = v
    79  	}
    80  	for _, s := range placeholders {
    81  		if _, ok := result[s]; ok {
    82  			continue
    83  		}
    84  		result[s] = ""
    85  	}
    86  	return result
    87  }
    88  
    89  // CounterVec is a family of Count metrics.
    90  type CounterVec struct {
    91  	*prometheus.CounterVec
    92  	Key            string
    93  	PossibleLabels []string
    94  }
    95  
    96  // AddPossibleLabels adds prometheus labels to the list of expected labels of a Metric of this family.
    97  func (v *CounterVec) AddPossibleLabels(newLabels []string) {
    98  	v.PossibleLabels = mergeSortedStrings(v.PossibleLabels, newLabels...)
    99  }
   100  
   101  // GetMetricWith returns a Metric with the values of labels as provided.
   102  func (v *CounterVec) GetMetricWith(labels prometheus.Labels) (prometheus.Counter, error) {
   103  	return v.CounterVec.GetMetricWith(labelsWithPlaceholders(labels, v.PossibleLabels))
   104  }
   105  
   106  // GaugeVec is a family of Gauge metrics.
   107  type GaugeVec struct {
   108  	*prometheus.GaugeVec
   109  	Key            string
   110  	PossibleLabels []string
   111  }
   112  
   113  // AddPossibleLabels adds prometheus labels to the list of expected labels of a Metric of this family.
   114  func (v *GaugeVec) AddPossibleLabels(newLabels []string) {
   115  	v.PossibleLabels = mergeSortedStrings(v.PossibleLabels, newLabels...)
   116  }
   117  
   118  // GetMetricWith returns a Metric with the values of labels as provided.
   119  func (v *GaugeVec) GetMetricWith(labels prometheus.Labels) (prometheus.Gauge, error) {
   120  	return v.GaugeVec.GetMetricWith(labelsWithPlaceholders(labels, v.PossibleLabels))
   121  }
   122  
   123  type persistentData struct {
   124  	storage
   125  	config
   126  }
   127  
   128  type storage struct {
   129  	locker   sync.Mutex
   130  	registry *prometheus.Registry
   131  	count    map[string]*CounterVec
   132  	gauge    map[string]*GaugeVec
   133  	intGauge map[string]*GaugeVec
   134  
   135  	// temporary buffers (placing here to avoid memory allocations)
   136  	tmpLabels        prometheus.Labels
   137  	tmpLabelNames    []string
   138  	tmpLabelNamesBuf []string
   139  }
   140  
   141  // Metrics implements a wrapper of prometheus metrics to implement
   142  // metrics.Metrics.
   143  //
   144  // Pretty slow and naive implementation. Could be improved by on-need basis.
   145  // If you need a faster implementation, then try `tsmetrics`.
   146  //
   147  // Warning! This implementation does not remove automatically metrics, thus
   148  // if a metric was created once, it will be kept in memory forever.
   149  // If you need a version of metrics which automatically removes metrics
   150  // non-used for a long time, then try `tsmetrics`.
   151  //
   152  // Warning! Prometheus does not support changing amount of labels for a metric,
   153  // therefore we delete and create a metric from scratch if it is required to
   154  // extend the set of labels. This procedure leads to a reset of the metric
   155  // value.
   156  // If you need a version of metrics which does not have such flaw, then
   157  // try `tsmetrics` or `simplemetrics`.
   158  type Metrics struct {
   159  	*persistentData
   160  	labels     prometheus.Labels
   161  	labelNames []string
   162  }
   163  
   164  // New returns a new instance of Metrics
   165  func New(registry *prometheus.Registry, opts ...Option) *Metrics {
   166  	m := &Metrics{
   167  		persistentData: &persistentData{
   168  			storage: storage{
   169  				registry:  registry,
   170  				count:     map[string]*CounterVec{},
   171  				gauge:     map[string]*GaugeVec{},
   172  				intGauge:  map[string]*GaugeVec{},
   173  				tmpLabels: make(prometheus.Labels),
   174  			},
   175  			config: options(opts).Config(),
   176  		},
   177  	}
   178  	return m
   179  }
   180  
   181  var (
   182  	alternativeDefaultRegistry     *prometheus.Registry
   183  	alternativeDefaultRegistryOnce sync.Once
   184  )
   185  
   186  // Default returns metrics.Metrics with the default configuration.
   187  var Default = func() metrics.Metrics {
   188  	registry, ok := prometheus.DefaultRegisterer.(*prometheus.Registry)
   189  	if !ok {
   190  		alternativeDefaultRegistryOnce.Do(func() {
   191  			alternativeDefaultRegistry = prometheus.NewRegistry()
   192  		})
   193  		registry = alternativeDefaultRegistry
   194  	}
   195  	return New(registry)
   196  }
   197  
   198  // List returns all the metrics. It could be used for a custom exporter.
   199  func (m *Metrics) List() []prometheus.Collector {
   200  	m.storage.locker.Lock()
   201  	defer m.storage.locker.Unlock()
   202  	result := make([]prometheus.Collector, 0, len(m.storage.count)+len(m.storage.gauge)+len(m.storage.intGauge))
   203  
   204  	for _, count := range m.storage.count {
   205  		result = append(result, count.CounterVec)
   206  	}
   207  
   208  	for _, gauge := range m.storage.gauge {
   209  		result = append(result, gauge.GaugeVec)
   210  	}
   211  
   212  	for _, intGauge := range m.storage.intGauge {
   213  		result = append(result, intGauge.GaugeVec)
   214  	}
   215  
   216  	return result
   217  }
   218  
   219  // Registerer returns prometheus.Registerer of this Metrics.
   220  func (m *Metrics) Registerer() prometheus.Registerer {
   221  	return m.registry
   222  }
   223  
   224  // Gatherer returns prometheus.Gatherer of this Metrics.
   225  func (m *Metrics) Gatherer() prometheus.Gatherer {
   226  	return m.registry
   227  }
   228  
   229  func (m *Metrics) getOrCreateCountVec(key string, possibleLabelNames []string) *CounterVec {
   230  	counterVec := m.count[key]
   231  	if counterVec != nil {
   232  		return counterVec
   233  	}
   234  
   235  	counterVec = &CounterVec{
   236  		CounterVec: prometheus.NewCounterVec(prometheus.CounterOpts{
   237  			Name: key + "_count",
   238  		}, possibleLabelNames),
   239  		Key:            key,
   240  		PossibleLabels: possibleLabelNames,
   241  	}
   242  
   243  	m.count[key] = counterVec
   244  
   245  	if m.registry != nil {
   246  		err := m.registry.Register(counterVec)
   247  		if err != nil {
   248  			panic(fmt.Sprintf("key: '%v', err: %v", key, err))
   249  		}
   250  	}
   251  
   252  	return counterVec
   253  }
   254  
   255  func (m *Metrics) deleteCountVec(counterVec *CounterVec) {
   256  	if m.registry != nil {
   257  		if !unregister(m.registry, counterVec.CounterVec) {
   258  			panic(counterVec)
   259  		}
   260  	}
   261  	delete(m.count, counterVec.Key)
   262  }
   263  
   264  // Count implements context.Metrics (see the description in the interface).
   265  func (m *Metrics) Count(key string) types.Count {
   266  	return m.CountFields(key, nil)
   267  }
   268  
   269  // CountFields returns Count metric given key and additional field values (on top of already defined).
   270  func (m *Metrics) CountFields(key string, addFields field.AbstractFields) types.Count {
   271  	m.storage.locker.Lock()
   272  	defer m.storage.locker.Unlock()
   273  	labels, labelNames := m.labelsWithAddFields(addFields)
   274  
   275  	counterVec := m.getOrCreateCountVec(key, labelNames)
   276  
   277  	counter, err := counterVec.GetMetricWith(labels)
   278  	if err != nil {
   279  		m.deleteCountVec(counterVec)
   280  		counterVec.AddPossibleLabels(labelNames)
   281  		counterVec = m.getOrCreateCountVec(key, counterVec.PossibleLabels)
   282  		counter, err = counterVec.GetMetricWith(labels)
   283  		if err != nil {
   284  			panic(err)
   285  		}
   286  	}
   287  
   288  	return &Count{Metrics: m, Key: key, CounterVec: counterVec, Counter: counter}
   289  }
   290  
   291  // ForEachCount iterates over each Count metric. Stops on first `false` returned by the callback.
   292  func (m *Metrics) ForEachCount(callback func(types.Count) bool) bool {
   293  	m.storage.locker.Lock()
   294  	defer m.storage.locker.Unlock()
   295  
   296  	shouldExitNow := false
   297  	for key, family := range m.storage.count {
   298  		ch := make(chan prometheus.Metric)
   299  		go func() {
   300  			family.CounterVec.Collect(ch)
   301  			close(ch)
   302  		}()
   303  		for abstractMetric := range ch {
   304  			metric := abstractMetric.(prometheus.Counter)
   305  			if !callback(&Count{Metrics: m, Key: key, CounterVec: family, Counter: metric}) {
   306  				shouldExitNow = true
   307  				break
   308  			}
   309  		}
   310  		for range ch {
   311  		}
   312  		if shouldExitNow {
   313  			return false
   314  		}
   315  	}
   316  	return true
   317  }
   318  
   319  func (m *Metrics) getOrCreateGaugeVec(key string, possibleLabelNames []string) *GaugeVec {
   320  	gaugeVec := m.gauge[key]
   321  	if gaugeVec != nil {
   322  		return gaugeVec
   323  	}
   324  
   325  	_gaugeVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{
   326  		Name: key + "_float",
   327  	}, possibleLabelNames)
   328  
   329  	gaugeVec = &GaugeVec{
   330  		GaugeVec:       _gaugeVec,
   331  		Key:            key,
   332  		PossibleLabels: possibleLabelNames,
   333  	}
   334  
   335  	m.gauge[key] = gaugeVec
   336  
   337  	if m.registry != nil {
   338  		err := m.registry.Register(gaugeVec)
   339  		if err != nil {
   340  			panic(fmt.Sprintf("key: '%v', err: %v", key, err))
   341  		}
   342  	}
   343  
   344  	return gaugeVec
   345  }
   346  
   347  func (m *Metrics) deleteGaugeVec(gaugeVec *GaugeVec) {
   348  	if m.registry != nil {
   349  		if !unregister(m.registry, gaugeVec.GaugeVec) {
   350  			panic(gaugeVec)
   351  		}
   352  	}
   353  	delete(m.gauge, gaugeVec.Key)
   354  }
   355  
   356  // expects m.storage.locker be Locked
   357  func (m *Metrics) labelsWithAddFields(addFields field.AbstractFields) (prometheus.Labels, []string) {
   358  	if addFields == nil {
   359  		return m.labels, m.labelNames
   360  	}
   361  
   362  	for k := range m.storage.tmpLabels {
   363  		delete(m.storage.tmpLabels, k)
   364  	}
   365  	for k, v := range m.labels {
   366  		m.storage.tmpLabels[k] = v
   367  	}
   368  	if cap(m.storage.tmpLabelNames) < len(m.labels) {
   369  		m.storage.tmpLabelNames = make([]string, len(m.labels)+addFields.Len())
   370  	}
   371  	m.storage.tmpLabelNames = m.tmpLabelNames[:len(m.labels)]
   372  	copy(m.storage.tmpLabelNames, m.labelNames)
   373  	addFields.ForEachField(func(f *field.Field) bool {
   374  		if !f.Properties.Has(types.FieldPropInclude) {
   375  			return true
   376  		}
   377  		v := FieldValueToString(f.Value)
   378  		if _, isSet := m.storage.tmpLabels[f.Key]; !isSet {
   379  			m.storage.tmpLabelNames = append(m.storage.tmpLabelNames, f.Key)
   380  		}
   381  		m.storage.tmpLabels[f.Key] = v
   382  		return true
   383  	})
   384  	unsortedCount := len(m.storage.tmpLabelNames) - len(m.labels)
   385  	if cap(m.storage.tmpLabelNamesBuf) < unsortedCount {
   386  		m.storage.tmpLabelNamesBuf = make([]string, unsortedCount)
   387  	}
   388  	m.storage.tmpLabelNamesBuf = m.storage.tmpLabelNamesBuf[:unsortedCount]
   389  	xsort.AppendedWithBuf(sort.StringSlice(m.storage.tmpLabelNames), m.storage.tmpLabelNamesBuf)
   390  	return m.storage.tmpLabels, m.storage.tmpLabelNames
   391  }
   392  
   393  // Gauge implements context.Metrics (see the description in the interface).
   394  func (m *Metrics) Gauge(key string) types.Gauge {
   395  	return m.GaugeFields(key, nil)
   396  }
   397  
   398  // GaugeFields returns Gauge metric given key and additional field values (on top of already defined).
   399  func (m *Metrics) GaugeFields(key string, addFields field.AbstractFields) types.Gauge {
   400  	m.storage.locker.Lock()
   401  	defer m.storage.locker.Unlock()
   402  	labels, labelNames := m.labelsWithAddFields(addFields)
   403  
   404  	gaugeVec := m.getOrCreateGaugeVec(key, labelNames)
   405  
   406  	gauge, err := gaugeVec.GetMetricWith(labels)
   407  	if err != nil {
   408  		m.deleteGaugeVec(gaugeVec)
   409  		gaugeVec.AddPossibleLabels(labelNames)
   410  		gaugeVec = m.getOrCreateGaugeVec(key, gaugeVec.PossibleLabels)
   411  		gauge, err = gaugeVec.GetMetricWith(labels)
   412  		if err != nil {
   413  			panic(err)
   414  		}
   415  	}
   416  
   417  	return &Gauge{Metrics: m, Key: key, GaugeVec: gaugeVec, Gauge: gauge}
   418  }
   419  
   420  // ForEachGauge iterates over each Gauge metric. Stops on first `false` returned by the callback.
   421  func (m *Metrics) ForEachGauge(callback func(types.Gauge) bool) bool {
   422  	m.storage.locker.Lock()
   423  	defer m.storage.locker.Unlock()
   424  
   425  	shouldExitNow := false
   426  	for key, family := range m.storage.gauge {
   427  		ch := make(chan prometheus.Metric)
   428  		go func() {
   429  			family.GaugeVec.Collect(ch)
   430  			close(ch)
   431  		}()
   432  		for abstractMetric := range ch {
   433  			metric := abstractMetric.(prometheus.Gauge)
   434  			if !callback(&Gauge{Metrics: m, Key: key, GaugeVec: family, Gauge: metric}) {
   435  				shouldExitNow = true
   436  				break
   437  			}
   438  		}
   439  		for range ch {
   440  		}
   441  		if shouldExitNow {
   442  			return false
   443  		}
   444  	}
   445  	return true
   446  }
   447  
   448  func (m *Metrics) getOrCreateIntGaugeVec(key string, possibleLabelNames []string) *GaugeVec {
   449  	gaugeVec := m.intGauge[key]
   450  	if gaugeVec != nil {
   451  		return gaugeVec
   452  	}
   453  
   454  	_gaugeVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{
   455  		Name: key + "_int",
   456  	}, possibleLabelNames)
   457  
   458  	gaugeVec = &GaugeVec{
   459  		GaugeVec:       _gaugeVec,
   460  		Key:            key,
   461  		PossibleLabels: possibleLabelNames,
   462  	}
   463  
   464  	m.intGauge[key] = gaugeVec
   465  
   466  	if m.registry != nil {
   467  		err := m.registry.Register(gaugeVec)
   468  		if err != nil {
   469  			panic(fmt.Sprintf("key: '%v', err: %v", key, err))
   470  		}
   471  	}
   472  
   473  	return gaugeVec
   474  }
   475  
   476  func (m *Metrics) deleteIntGaugeVec(intGaugeVec *GaugeVec) {
   477  	if m.registry != nil {
   478  		if !unregister(m.registry, intGaugeVec) {
   479  			panic(intGaugeVec)
   480  		}
   481  	}
   482  	delete(m.intGauge, intGaugeVec.Key)
   483  }
   484  
   485  // IntGauge implements context.Metrics (see the description in the interface).
   486  func (m *Metrics) IntGauge(key string) types.IntGauge {
   487  	return m.IntGaugeFields(key, nil)
   488  }
   489  
   490  // IntGaugeFields returns IntGauge metric given key and additional field values (on top of already defined).
   491  func (m *Metrics) IntGaugeFields(key string, addFields field.AbstractFields) types.IntGauge {
   492  	m.storage.locker.Lock()
   493  	defer m.storage.locker.Unlock()
   494  	labels, labelNames := m.labelsWithAddFields(addFields)
   495  
   496  	gaugeVec := m.getOrCreateIntGaugeVec(key, labelNames)
   497  
   498  	gauge, err := gaugeVec.GetMetricWith(labels)
   499  	if err != nil {
   500  		m.deleteIntGaugeVec(gaugeVec)
   501  		gaugeVec.AddPossibleLabels(labelNames)
   502  		gaugeVec = m.getOrCreateIntGaugeVec(key, gaugeVec.PossibleLabels)
   503  		gauge, err = gaugeVec.GetMetricWith(labels)
   504  		if err != nil {
   505  			panic(err)
   506  		}
   507  	}
   508  
   509  	return &IntGauge{Metrics: m, Key: key, GaugeVec: gaugeVec, Gauge: gauge}
   510  }
   511  
   512  // ForEachIntGauge iterates over each IntGauge metric. Stops on first `false` returned by the callback.
   513  func (m *Metrics) ForEachIntGauge(callback func(types.IntGauge) bool) bool {
   514  	m.storage.locker.Lock()
   515  	defer m.storage.locker.Unlock()
   516  
   517  	shouldExitNow := false
   518  	for key, family := range m.storage.intGauge {
   519  		ch := make(chan prometheus.Metric)
   520  		go func() {
   521  			family.GaugeVec.Collect(ch)
   522  			close(ch)
   523  		}()
   524  		for abstractMetric := range ch {
   525  			metric := abstractMetric.(prometheus.Gauge)
   526  			if !callback(&IntGauge{Metrics: m, Key: key, GaugeVec: family, Gauge: metric}) {
   527  				shouldExitNow = true
   528  				break
   529  			}
   530  		}
   531  		for range ch {
   532  		}
   533  		if shouldExitNow {
   534  			return false
   535  		}
   536  	}
   537  	return true
   538  }
   539  
   540  // WithContextFields implements metrics.Metrics.
   541  func (m *Metrics) WithContextFields(allFields *field.FieldsChain, newFieldsCount int) belt.Tool {
   542  	if m.config.DisableLabels {
   543  		return m
   544  	}
   545  
   546  	result := &Metrics{
   547  		persistentData: m.persistentData,
   548  	}
   549  	var (
   550  		labels prometheus.Labels
   551  		names  []string
   552  	)
   553  	if newFieldsCount == -1 {
   554  		result.labels = nil
   555  		result.labelNames = nil
   556  		if allFields == nil {
   557  			return result
   558  		}
   559  		labels, names = result.labelsWithAddFields(allFields)
   560  	} else {
   561  		labels, names = m.labelsWithAddFields(field.NewSlicer(allFields, 0, uint(newFieldsCount)))
   562  	}
   563  	result.labels = make(prometheus.Labels, len(labels))
   564  	for k, v := range labels {
   565  		result.labels[k] = v
   566  	}
   567  	result.labelNames = make([]string, 0, len(names))
   568  	result.labelNames = append(result.labelNames, names...)
   569  	return result
   570  }
   571  
   572  // WithTraceIDs implements metrics.Metrics.
   573  func (m *Metrics) WithTraceIDs(traceIDs belt.TraceIDs, newTraceIDsCount int) belt.Tool {
   574  	// Should be ignored per metrics.Metrics interface description, so returning
   575  	// as is:
   576  	return m
   577  }
   578  
   579  // Flush implements metrics.Metrics (or more specifically belt.Tool).
   580  func (*Metrics) Flush() {}
   581  
   582  func fieldsToLabels(fields field.AbstractFields) prometheus.Labels {
   583  	labels := make(prometheus.Labels, fields.Len())
   584  	fields.ForEachField(func(f *field.Field) bool {
   585  		labels[f.Key] = FieldValueToString(f.Value)
   586  		return true
   587  	})
   588  	return labels
   589  }
   590  
   591  const prebakeMax = 65536
   592  
   593  var prebackedString [prebakeMax * 2]string
   594  
   595  func init() {
   596  	for i := -prebakeMax; i < prebakeMax; i++ {
   597  		prebackedString[i+prebakeMax] = strconv.FormatInt(int64(i), 10)
   598  	}
   599  }
   600  
   601  func getPrebakedString(v int32) string {
   602  	if v >= prebakeMax || -v <= -prebakeMax {
   603  		return ""
   604  	}
   605  	return prebackedString[v+prebakeMax]
   606  }
   607  
   608  // FieldValueToString converts any value to a string, which could be used
   609  // as label value in prometheus.
   610  func FieldValueToString(vI interface{}) string {
   611  	switch v := vI.(type) {
   612  	case int:
   613  		r := getPrebakedString(int32(v))
   614  		if len(r) != 0 {
   615  			return r
   616  		}
   617  		return strconv.FormatInt(int64(v), 10)
   618  	case uint64:
   619  		r := getPrebakedString(int32(v))
   620  		if len(r) != 0 {
   621  			return r
   622  		}
   623  		return strconv.FormatUint(v, 10)
   624  	case int64:
   625  		r := getPrebakedString(int32(v))
   626  		if len(r) != 0 {
   627  			return r
   628  		}
   629  		return strconv.FormatInt(v, 10)
   630  	case string:
   631  		return strings.Replace(v, ",", "_", -1)
   632  	case bool:
   633  		switch v {
   634  		case true:
   635  			return "true"
   636  		case false:
   637  			return "false"
   638  		}
   639  	case []byte:
   640  		return string(v)
   641  	case nil:
   642  		return "null"
   643  	case interface{ String() string }:
   644  		return strings.Replace(v.String(), ",", "_", -1)
   645  	}
   646  
   647  	return "<unknown_type>"
   648  }