Path: blob/main/component/loki/process/internal/metric/counters.go
4096 views
package metric12import (3"fmt"4"time"56"github.com/prometheus/client_golang/prometheus"7"github.com/prometheus/common/model"8)910const (11CounterInc = "inc"12CounterAdd = "add"13)1415// CounterConfig defines a counter metric whose value only goes up.16type CounterConfig struct {17// Shared fields18Name string `river:"name,attr"`19Description string `river:"description,attr,optional"`20Source string `river:"source,attr,optional"`21Prefix string `river:"prefix,attr,optional"`22MaxIdle time.Duration `river:"max_idle_duration,attr,optional"`23Value string `river:"value,attr,optional"`2425// Counter-specific fields26Action string `river:"action,attr"`27MatchAll bool `river:"match_all,attr,optional"`28CountEntryBytes bool `river:"count_entry_bytes,attr,optional"`29}3031// DefaultCounterConfig sets the default for a Counter.32var DefaultCounterConfig = CounterConfig{33MaxIdle: 5 * time.Minute,34}3536// UnmarshalRiver implements the unmarshaller37func (c *CounterConfig) UnmarshalRiver(f func(v interface{}) error) error {38*c = DefaultCounterConfig39type counter CounterConfig40err := f((*counter)(c))41if err != nil {42return err43}4445if c.MaxIdle < 1*time.Second {46return fmt.Errorf("max_idle_duration must be greater or equal than 1s")47}4849if c.Source == "" {50c.Source = c.Name51}52if c.Action != CounterInc && c.Action != CounterAdd {53return fmt.Errorf("the 'action' counter field must be either 'inc' or 'add'")54}5556if c.MatchAll && c.Value != "" {57return fmt.Errorf("a 'counter' metric supports either 'match_all' or a 'value', but not both")58}59if c.CountEntryBytes && (!c.MatchAll && c.Action != "add") {60return fmt.Errorf("the 'count_entry_bytes' counter field must be specified along with match_all set to true or action set to 'add'")61}62return nil63}6465// Counters is a vector of counters for a log stream.66type Counters struct {67*metricVec68Cfg *CounterConfig69}7071// NewCounters creates a new counter vec.72func NewCounters(name string, config *CounterConfig) (*Counters, error) {73return &Counters{74metricVec: newMetricVec(func(labels map[string]string) prometheus.Metric {75return &expiringCounter{prometheus.NewCounter(prometheus.CounterOpts{76Help: config.Description,77Name: name,78ConstLabels: labels,79}),800,81}82}, int64(config.MaxIdle.Seconds())),83Cfg: config,84}, nil85}8687// With returns the counter associated with a stream labelset.88func (c *Counters) With(labels model.LabelSet) prometheus.Counter {89return c.metricVec.With(labels).(prometheus.Counter)90}9192type expiringCounter struct {93prometheus.Counter94lastModSec int6495}9697// Inc increments the counter by 1. Use Add to increment it by arbitrary98// non-negative values.99func (e *expiringCounter) Inc() {100e.Counter.Inc()101e.lastModSec = time.Now().Unix()102}103104// Add adds the given value to the counter. It panics if the value is <105// 0.106func (e *expiringCounter) Add(val float64) {107e.Counter.Add(val)108e.lastModSec = time.Now().Unix()109}110111// HasExpired implements Expirable112func (e *expiringCounter) HasExpired(currentTimeSec int64, maxAgeSec int64) bool {113return currentTimeSec-e.lastModSec >= maxAgeSec114}115116117