github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/logentry/metric/counters.go (about) 1 package metric 2 3 import ( 4 "strings" 5 "time" 6 7 "github.com/mitchellh/mapstructure" 8 "github.com/pkg/errors" 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/prometheus/common/model" 11 ) 12 13 const ( 14 CounterInc = "inc" 15 CounterAdd = "add" 16 17 ErrCounterActionRequired = "counter action must be defined as either `inc` or `add`" 18 ErrCounterInvalidAction = "action %s is not valid, action must be either `inc` or `add`" 19 ErrCounterInvalidMatchAll = "`match_all: true` cannot be combined with `value`, please remove `match_all` or `value`" 20 ErrCounterInvalidCountBytes = "`count_entry_bytes: true` can only be set with `match_all: true`" 21 ErrCounterInvalidCountBytesAction = "`count_entry_bytes: true` can only be used with `action: add`" 22 ) 23 24 type CounterConfig struct { 25 MatchAll *bool `mapstructure:"match_all"` 26 CountBytes *bool `mapstructure:"count_entry_bytes"` 27 Value *string `mapstructure:"value"` 28 Action string `mapstructure:"action"` 29 } 30 31 func validateCounterConfig(config *CounterConfig) error { 32 if config.Action == "" { 33 return errors.New(ErrCounterActionRequired) 34 } 35 config.Action = strings.ToLower(config.Action) 36 if config.Action != CounterInc && config.Action != CounterAdd { 37 return errors.Errorf(ErrCounterInvalidAction, config.Action) 38 } 39 if config.MatchAll != nil && *config.MatchAll && config.Value != nil { 40 return errors.Errorf(ErrCounterInvalidMatchAll) 41 } 42 if config.CountBytes != nil && *config.CountBytes && (config.MatchAll == nil || !*config.MatchAll) { 43 return errors.New(ErrCounterInvalidCountBytes) 44 } 45 if config.CountBytes != nil && *config.CountBytes && config.Action != CounterAdd { 46 return errors.New(ErrCounterInvalidCountBytesAction) 47 } 48 return nil 49 } 50 51 func parseCounterConfig(config interface{}) (*CounterConfig, error) { 52 cfg := &CounterConfig{} 53 err := mapstructure.Decode(config, cfg) 54 if err != nil { 55 return nil, err 56 } 57 return cfg, nil 58 } 59 60 // Counters is a vec tor of counters for a each log stream. 61 type Counters struct { 62 *metricVec 63 Cfg *CounterConfig 64 } 65 66 // NewCounters creates a new counter vec. 67 func NewCounters(name, help string, config interface{}, maxIdleSec int64) (*Counters, error) { 68 cfg, err := parseCounterConfig(config) 69 if err != nil { 70 return nil, err 71 } 72 err = validateCounterConfig(cfg) 73 if err != nil { 74 return nil, err 75 } 76 return &Counters{ 77 metricVec: newMetricVec(func(labels map[string]string) prometheus.Metric { 78 return &expiringCounter{prometheus.NewCounter(prometheus.CounterOpts{ 79 Help: help, 80 Name: name, 81 ConstLabels: labels, 82 }), 83 0, 84 } 85 }, maxIdleSec), 86 Cfg: cfg, 87 }, nil 88 } 89 90 // With returns the counter associated with a stream labelset. 91 func (c *Counters) With(labels model.LabelSet) prometheus.Counter { 92 return c.metricVec.With(labels).(prometheus.Counter) 93 } 94 95 type expiringCounter struct { 96 prometheus.Counter 97 lastModSec int64 98 } 99 100 // Inc increments the counter by 1. Use Add to increment it by arbitrary 101 // non-negative values. 102 func (e *expiringCounter) Inc() { 103 e.Counter.Inc() 104 e.lastModSec = time.Now().Unix() 105 } 106 107 // Add adds the given value to the counter. It panics if the value is < 108 // 0. 109 func (e *expiringCounter) Add(val float64) { 110 e.Counter.Add(val) 111 e.lastModSec = time.Now().Unix() 112 } 113 114 // HasExpired implements Expirable 115 func (e *expiringCounter) HasExpired(currentTimeSec int64, maxAgeSec int64) bool { 116 return currentTimeSec-e.lastModSec >= maxAgeSec 117 }