vitess.io/vitess@v0.16.2/go/stats/prometheusbackend/prometheusbackend.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package prometheusbackend
    18  
    19  import (
    20  	"expvar"
    21  	"net/http"
    22  	"strings"
    23  
    24  	"github.com/prometheus/client_golang/prometheus"
    25  	"github.com/prometheus/client_golang/prometheus/promhttp"
    26  
    27  	"vitess.io/vitess/go/stats"
    28  	"vitess.io/vitess/go/vt/log"
    29  )
    30  
    31  // PromBackend implements PullBackend using Prometheus as the backing metrics storage.
    32  type PromBackend struct {
    33  	namespace string
    34  }
    35  
    36  var (
    37  	be PromBackend
    38  )
    39  
    40  // Init initializes the Prometheus be with the given namespace.
    41  func Init(namespace string) {
    42  	http.Handle("/metrics", promhttp.Handler())
    43  	be.namespace = namespace
    44  	stats.Register(be.publishPrometheusMetric)
    45  }
    46  
    47  // PublishPromMetric is used to publish the metric to Prometheus.
    48  func (be PromBackend) publishPrometheusMetric(name string, v expvar.Var) {
    49  	switch st := v.(type) {
    50  	case *stats.Counter:
    51  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return float64(st.Get()) })
    52  	case *stats.CounterFunc:
    53  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return float64(st.F()) })
    54  	case *stats.Gauge:
    55  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return float64(st.Get()) })
    56  	case *stats.GaugeFloat64:
    57  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return st.Get() })
    58  	case *stats.GaugeFunc:
    59  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return float64(st.F()) })
    60  	case stats.FloatFunc:
    61  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return (st)() })
    62  	case *stats.CountersWithSingleLabel:
    63  		newCountersWithSingleLabelCollector(st, be.buildPromName(name), st.Label(), prometheus.CounterValue)
    64  	case *stats.CountersWithMultiLabels:
    65  		newMetricWithMultiLabelsCollector(st, be.buildPromName(name))
    66  	case *stats.CountersFuncWithMultiLabels:
    67  		newMetricsFuncWithMultiLabelsCollector(st, be.buildPromName(name), prometheus.CounterValue)
    68  	case *stats.GaugesFuncWithMultiLabels:
    69  		newMetricsFuncWithMultiLabelsCollector(&st.CountersFuncWithMultiLabels, be.buildPromName(name), prometheus.GaugeValue)
    70  	case *stats.GaugesWithSingleLabel:
    71  		newGaugesWithSingleLabelCollector(st, be.buildPromName(name), st.Label(), prometheus.GaugeValue)
    72  	case *stats.GaugesWithMultiLabels:
    73  		newGaugesWithMultiLabelsCollector(st, be.buildPromName(name))
    74  	case *stats.CounterDuration:
    75  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return st.Get().Seconds() })
    76  	case *stats.CounterDurationFunc:
    77  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return st.F().Seconds() })
    78  	case *stats.GaugeDuration:
    79  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return st.Get().Seconds() })
    80  	case *stats.GaugeDurationFunc:
    81  		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return st.F().Seconds() })
    82  	case *stats.Timings:
    83  		newTimingsCollector(st, be.buildPromName(name))
    84  	case *stats.MultiTimings:
    85  		newMultiTimingsCollector(st, be.buildPromName(name))
    86  	case *stats.Histogram:
    87  		newHistogramCollector(st, be.buildPromName(name))
    88  	case *stats.String, stats.StringFunc, stats.StringMapFunc, *stats.Rates, *stats.RatesFunc:
    89  		// Silently ignore these types since they don't make sense to
    90  		// export to Prometheus' data model.
    91  	default:
    92  		log.Fatalf("prometheus: Metric type %T (seen for variable: %s) is not covered by type switch. Add it there and to all other plugins which register a NewVarHook.", st, name)
    93  	}
    94  }
    95  
    96  // buildPromName specifies the namespace as a prefix to the metric name
    97  func (be PromBackend) buildPromName(name string) string {
    98  	s := strings.TrimPrefix(normalizeMetric(name), be.namespace+"_")
    99  	return prometheus.BuildFQName("", be.namespace, s)
   100  }
   101  
   102  func labelsToSnake(labels []string) []string {
   103  	output := make([]string, len(labels))
   104  	for i, l := range labels {
   105  		output[i] = normalizeMetric(l)
   106  	}
   107  	return output
   108  }
   109  
   110  // normalizeMetricForPrometheus produces a compliant name by applying
   111  // special case conversions and then applying a camel case to snake case converter.
   112  func normalizeMetric(name string) string {
   113  	// Special cases
   114  	r := strings.NewReplacer("VSchema", "vschema", "VtGate", "vtgate")
   115  	name = r.Replace(name)
   116  
   117  	return stats.GetSnakeName(name)
   118  }