github.com/thanos-io/thanos@v0.32.5/pkg/extprom/tx_gauge.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package extprom 5 6 import ( 7 "sync" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/prometheus/client_golang/prometheus/promauto" 11 ) 12 13 type TxGaugeVec struct { 14 current *prometheus.GaugeVec 15 mtx sync.Mutex 16 newMetricVal func() *prometheus.GaugeVec 17 18 tx *prometheus.GaugeVec 19 } 20 21 // NewTxGaugeVec is a prometheus.GaugeVec that allows to start atomic metric value transaction. 22 // It might be useful if long process that wants to update a GaugeVec but wants to build/accumulate those metrics 23 // in a concurrent way without exposing partial state to Prometheus. 24 // Caller can also use this as normal GaugeVec. 25 // 26 // Additionally it allows to init LabelValues on each transaction. 27 // NOTE: This is quite naive implementation creating new prometheus.GaugeVec on each `ResetTx`, use wisely. 28 func NewTxGaugeVec(reg prometheus.Registerer, opts prometheus.GaugeOpts, labelNames []string, initLabelValues ...[]string) *TxGaugeVec { 29 // Nil as we will register it on our own later. 30 f := func() *prometheus.GaugeVec { 31 g := promauto.With(nil).NewGaugeVec(opts, labelNames) 32 for _, vals := range initLabelValues { 33 g.WithLabelValues(vals...) 34 } 35 return g 36 } 37 tx := &TxGaugeVec{ 38 current: f(), 39 newMetricVal: f, 40 } 41 if reg != nil { 42 reg.MustRegister(tx) 43 } 44 return tx 45 } 46 47 // ResetTx starts new transaction. Not goroutine-safe. 48 func (tx *TxGaugeVec) ResetTx() { 49 tx.tx = tx.newMetricVal() 50 } 51 52 // Submit atomically and fully applies new values from existing transaction GaugeVec. Not goroutine-safe. 53 func (tx *TxGaugeVec) Submit() { 54 if tx.tx == nil { 55 return 56 } 57 58 tx.mtx.Lock() 59 tx.current = tx.tx 60 tx.mtx.Unlock() 61 } 62 63 // Describe is used in Register. 64 func (tx *TxGaugeVec) Describe(ch chan<- *prometheus.Desc) { 65 tx.mtx.Lock() 66 defer tx.mtx.Unlock() 67 68 tx.current.Describe(ch) 69 } 70 71 // Collect is used by Registered. 72 func (tx *TxGaugeVec) Collect(ch chan<- prometheus.Metric) { 73 tx.mtx.Lock() 74 defer tx.mtx.Unlock() 75 76 tx.current.Collect(ch) 77 } 78 79 // With works as GetMetricWith, but panics where GetMetricWithLabels would have 80 // returned an error. Not returning an error allows shortcuts like 81 // 82 // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) 83 func (tx *TxGaugeVec) With(labels prometheus.Labels) prometheus.Gauge { 84 if tx.tx == nil { 85 tx.ResetTx() 86 } 87 return tx.tx.With(labels) 88 } 89 90 // WithLabelValues works as GetMetricWithLabelValues, but panics where 91 // GetMetricWithLabelValues would have returned an error. Not returning an 92 // error allows shortcuts like 93 // 94 // myVec.WithLabelValues("404", "GET").Add(42) 95 func (tx *TxGaugeVec) WithLabelValues(lvs ...string) prometheus.Gauge { 96 if tx.tx == nil { 97 tx.ResetTx() 98 } 99 return tx.tx.WithLabelValues(lvs...) 100 }