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 }