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  }