github.com/puellanivis/breton@v0.2.16/lib/metrics/label.go (about)

     1  package metrics
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"sort"
     7  
     8  	"github.com/puellanivis/breton/lib/metrics/internal/kv"
     9  )
    10  
    11  // cannot use `const LabelVar = Label("asdf")` if Label is a function that
    12  // tests at compile-time. So, we will check at Registration time.
    13  var validLabel = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
    14  
    15  // A Labeler returns the name=value pairting from the implementer.
    16  type Labeler interface {
    17  	Label() (name, value string)
    18  }
    19  
    20  // Label describes only a label name, in a way that allows it to be set to a const.
    21  type Label string
    22  
    23  // Label implements Labeler. It returns an empty string for value.
    24  func (l Label) Label() (name, value string) {
    25  	return string(l), ""
    26  }
    27  
    28  // WithValue takes the given Label, and attaches a value to it.
    29  func (l Label) WithValue(value string) Labeler {
    30  	return LabelValue{
    31  		key: string(l),
    32  		val: value,
    33  	}
    34  }
    35  
    36  // Const returns a Labeler that defines a label with a constant value.
    37  // The concrete type is unexported in order to further enforce immutability.
    38  func (l Label) Const(value string) Labeler {
    39  	return constLabel{
    40  		key: string(l),
    41  		val: value,
    42  	}
    43  }
    44  
    45  // A LabelValue describes a complete pair of Label name and value.
    46  type LabelValue struct {
    47  	key, val string
    48  }
    49  
    50  // Label returns the name and value of the LabelValue.
    51  func (l LabelValue) Label() (name, value string) {
    52  	return l.key, l.val
    53  }
    54  
    55  // WithValue returns a new Labeler with the same Label name, but a new value.
    56  func (l LabelValue) WithValue(value string) Labeler {
    57  	return LabelValue{
    58  		key: l.key,
    59  		val: value,
    60  	}
    61  }
    62  
    63  type constLabel LabelValue
    64  
    65  func (l constLabel) Label() (name, value string) {
    66  	return l.key, l.val
    67  }
    68  
    69  // labelSet describes a set of labels, i.e. which keys are valid, and whether they may be set.
    70  type labelSet struct {
    71  	keys   []string
    72  	canSet map[string]bool
    73  }
    74  
    75  func newLabelSet(labels []Labeler) *labelSet {
    76  	s := &labelSet{
    77  		canSet: make(map[string]bool),
    78  	}
    79  
    80  	for _, label := range labels {
    81  		k, _ := label.Label()
    82  
    83  		if !validLabel.MatchString(k) {
    84  			panic(fmt.Sprintf("label name %q is invalid", k))
    85  		}
    86  
    87  		if _, ok := s.canSet[k]; ok {
    88  			panic(fmt.Sprintf("label %q redefined", k))
    89  		}
    90  
    91  		// Let’s assume it can be set
    92  		s.canSet[k] = true
    93  
    94  		if _, ok := label.(constLabel); ok {
    95  			// Well, ok then, it is a constant.
    96  			s.canSet[k] = false
    97  		}
    98  	}
    99  
   100  	s.keys = nil // fastest way to empty the list
   101  	for key := range s.canSet {
   102  		s.keys = append(s.keys, key)
   103  	}
   104  	sort.Strings(s.keys)
   105  
   106  	return s
   107  }
   108  
   109  // labelScope allows for the scoping of labels, meaning successive levels
   110  // of labels may be applied one after another.
   111  type labelScope struct {
   112  	set *labelSet   // keep track of the labelSet, for canSet testing
   113  	p   *labelScope // keep track of the parent of this scope
   114  
   115  	kv kv.KeyVal // the key:val set defined at this scope.
   116  }
   117  
   118  // defineLabels takes a list of Labelers, constructs an appropriate labelSet,
   119  // and then returns a labelScope containing the Constant and Default label values.
   120  func defineLabels(labels ...Labeler) *labelScope {
   121  	l := &labelScope{
   122  		set: newLabelSet(labels),
   123  	}
   124  
   125  	for _, label := range labels {
   126  		if k, v := label.Label(); v != "" {
   127  			l.kv.Append(k, v)
   128  		}
   129  	}
   130  	sort.Sort(l.kv)
   131  
   132  	return l
   133  }
   134  
   135  // With returns a child labelScope that has the given Labelers additionally set.
   136  func (l *labelScope) With(labels ...Labeler) *labelScope {
   137  	if l == nil {
   138  		panic("metric does not define any labels")
   139  	}
   140  
   141  	n := &labelScope{
   142  		set: l.set,
   143  		p:   l,
   144  	}
   145  
   146  	for _, label := range labels {
   147  		k, v := label.Label()
   148  
   149  		canSet, ok := n.set.canSet[k]
   150  		if !ok {
   151  			panic(fmt.Sprintf("attempt to assign to undefined label %q", k))
   152  		}
   153  
   154  		if !canSet {
   155  			panic(fmt.Sprintf("attempt to assign to constant label %q", k))
   156  		}
   157  
   158  		n.kv.Append(k, v)
   159  	}
   160  	sort.Sort(n.kv)
   161  
   162  	return n
   163  }
   164  
   165  // get returns the most-recently-set value for a given Label name.
   166  func (l *labelScope) get(name string) Labeler {
   167  	if i, ok := l.kv.Index(name); ok {
   168  		return LabelValue{
   169  			key: name,
   170  			val: l.kv.Vals[i],
   171  		}
   172  	}
   173  
   174  	if l.p != nil {
   175  		return l.p.get(name)
   176  	}
   177  
   178  	return Label(name)
   179  }
   180  
   181  // getList returns a slice of Labelers that defines the labels and their values.
   182  func (l *labelScope) getList() []Labeler {
   183  	var list []Labeler
   184  
   185  	for _, k := range l.set.keys {
   186  		_, v := l.get(k).Label()
   187  
   188  		list = append(list, LabelValue{
   189  			key: k,
   190  			val: v,
   191  		})
   192  	}
   193  
   194  	return list
   195  }
   196  
   197  // getMap is a conversion function necessary to wrap the prometheus client.
   198  func (l *labelScope) getMap() map[string]string {
   199  	m := make(map[string]string)
   200  
   201  	for _, k := range l.set.keys {
   202  		_, v := l.get(k).Label()
   203  
   204  		m[k] = v
   205  	}
   206  
   207  	return m
   208  }