github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/scrape/labels/labels.go (about)

     1  // Copyright 2017 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package labels
    15  
    16  import (
    17  	"bytes"
    18  	"encoding/json"
    19  	"sort"
    20  	"strconv"
    21  
    22  	"github.com/cespare/xxhash/v2"
    23  )
    24  
    25  // Well-known label names used by Prometheus components.
    26  const (
    27  	MetricName   = "__name__"
    28  	AlertName    = "alertname"
    29  	BucketLabel  = "le"
    30  	InstanceName = "instance"
    31  
    32  	labelSep = '\xfe'
    33  )
    34  
    35  var seps = []byte{'\xff'}
    36  
    37  // Label is a key/value pair of strings.
    38  type Label struct {
    39  	Name, Value string
    40  }
    41  
    42  // Labels is a sorted set of labels. Order has to be guaranteed upon
    43  // instantiation.
    44  type Labels []Label
    45  
    46  func (ls Labels) Len() int           { return len(ls) }
    47  func (ls Labels) Swap(i, j int)      { ls[i], ls[j] = ls[j], ls[i] }
    48  func (ls Labels) Less(i, j int) bool { return ls[i].Name < ls[j].Name }
    49  
    50  func (ls Labels) String() string {
    51  	var b bytes.Buffer
    52  
    53  	b.WriteByte('{')
    54  	for i, l := range ls {
    55  		if i > 0 {
    56  			b.WriteByte(',')
    57  			b.WriteByte(' ')
    58  		}
    59  		b.WriteString(l.Name)
    60  		b.WriteByte('=')
    61  		b.WriteString(strconv.Quote(l.Value))
    62  	}
    63  	b.WriteByte('}')
    64  	return b.String()
    65  }
    66  
    67  // Bytes returns ls as a byte slice.
    68  // It uses an byte invalid character as a separator and so should not be used for printing.
    69  func (ls Labels) Bytes(buf []byte) []byte {
    70  	b := bytes.NewBuffer(buf[:0])
    71  	b.WriteByte(labelSep)
    72  	for i, l := range ls {
    73  		if i > 0 {
    74  			b.WriteByte(seps[0])
    75  		}
    76  		b.WriteString(l.Name)
    77  		b.WriteByte(seps[0])
    78  		b.WriteString(l.Value)
    79  	}
    80  	return b.Bytes()
    81  }
    82  
    83  // MarshalJSON implements json.Marshaler.
    84  func (ls Labels) MarshalJSON() ([]byte, error) {
    85  	return json.Marshal(ls.Map())
    86  }
    87  
    88  // UnmarshalJSON implements json.Unmarshaler.
    89  func (ls *Labels) UnmarshalJSON(b []byte) error {
    90  	var m map[string]string
    91  
    92  	if err := json.Unmarshal(b, &m); err != nil {
    93  		return err
    94  	}
    95  
    96  	*ls = FromMap(m)
    97  	return nil
    98  }
    99  
   100  // MarshalYAML implements yaml.Marshaler.
   101  func (ls Labels) MarshalYAML() (interface{}, error) {
   102  	return ls.Map(), nil
   103  }
   104  
   105  // UnmarshalYAML implements yaml.Unmarshaler.
   106  func (ls *Labels) UnmarshalYAML(unmarshal func(interface{}) error) error {
   107  	var m map[string]string
   108  
   109  	if err := unmarshal(&m); err != nil {
   110  		return err
   111  	}
   112  
   113  	*ls = FromMap(m)
   114  	return nil
   115  }
   116  
   117  // MatchLabels returns a subset of Labels that matches/does not match with the provided label names based on the 'on' boolean.
   118  // If on is set to true, it returns the subset of labels that match with the provided label names and its inverse when 'on' is set to false.
   119  func (ls Labels) MatchLabels(on bool, names ...string) Labels {
   120  	matchedLabels := Labels{}
   121  
   122  	nameSet := map[string]struct{}{}
   123  	for _, n := range names {
   124  		nameSet[n] = struct{}{}
   125  	}
   126  
   127  	for _, v := range ls {
   128  		if _, ok := nameSet[v.Name]; on == ok && (on || v.Name != MetricName) {
   129  			matchedLabels = append(matchedLabels, v)
   130  		}
   131  	}
   132  
   133  	return matchedLabels
   134  }
   135  
   136  // Hash returns a hash value for the label set.
   137  func (ls Labels) Hash() uint64 {
   138  	// Use xxhash.Sum64(b) for fast path as it's faster.
   139  	b := make([]byte, 0, 1024)
   140  	for i, v := range ls {
   141  		if len(b)+len(v.Name)+len(v.Value)+2 >= cap(b) {
   142  			// If labels entry is 1KB+ do not allocate whole entry.
   143  			h := xxhash.New()
   144  			_, _ = h.Write(b)
   145  			for _, v := range ls[i:] {
   146  				_, _ = h.WriteString(v.Name)
   147  				_, _ = h.Write(seps)
   148  				_, _ = h.WriteString(v.Value)
   149  				_, _ = h.Write(seps)
   150  			}
   151  			return h.Sum64()
   152  		}
   153  
   154  		b = append(b, v.Name...)
   155  		b = append(b, seps[0])
   156  		b = append(b, v.Value...)
   157  		b = append(b, seps[0])
   158  	}
   159  	return xxhash.Sum64(b)
   160  }
   161  
   162  // HashForLabels returns a hash value for the labels matching the provided names.
   163  // 'names' have to be sorted in ascending order.
   164  func (ls Labels) HashForLabels(b []byte, names ...string) (uint64, []byte) {
   165  	b = b[:0]
   166  	i, j := 0, 0
   167  	for i < len(ls) && j < len(names) {
   168  		if names[j] < ls[i].Name {
   169  			j++
   170  		} else if ls[i].Name < names[j] {
   171  			i++
   172  		} else {
   173  			b = append(b, ls[i].Name...)
   174  			b = append(b, seps[0])
   175  			b = append(b, ls[i].Value...)
   176  			b = append(b, seps[0])
   177  			i++
   178  			j++
   179  		}
   180  	}
   181  	return xxhash.Sum64(b), b
   182  }
   183  
   184  // HashWithoutLabels returns a hash value for all labels except those matching
   185  // the provided names.
   186  // 'names' have to be sorted in ascending order.
   187  func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) {
   188  	b = b[:0]
   189  	j := 0
   190  	for i := range ls {
   191  		for j < len(names) && names[j] < ls[i].Name {
   192  			j++
   193  		}
   194  		if ls[i].Name == MetricName || (j < len(names) && ls[i].Name == names[j]) {
   195  			continue
   196  		}
   197  		b = append(b, ls[i].Name...)
   198  		b = append(b, seps[0])
   199  		b = append(b, ls[i].Value...)
   200  		b = append(b, seps[0])
   201  	}
   202  	return xxhash.Sum64(b), b
   203  }
   204  
   205  // WithLabels returns a new labels.Labels from ls that only contains labels matching names.
   206  // 'names' have to be sorted in ascending order.
   207  func (ls Labels) WithLabels(names ...string) Labels {
   208  	ret := make([]Label, 0, len(ls))
   209  
   210  	i, j := 0, 0
   211  	for i < len(ls) && j < len(names) {
   212  		if names[j] < ls[i].Name {
   213  			j++
   214  		} else if ls[i].Name < names[j] {
   215  			i++
   216  		} else {
   217  			ret = append(ret, ls[i])
   218  			i++
   219  			j++
   220  		}
   221  	}
   222  	return ret
   223  }
   224  
   225  // WithoutLabels returns a new labels.Labels from ls that contains labels not matching names.
   226  // 'names' have to be sorted in ascending order.
   227  func (ls Labels) WithoutLabels(names ...string) Labels {
   228  	ret := make([]Label, 0, len(ls))
   229  
   230  	j := 0
   231  	for i := range ls {
   232  		for j < len(names) && names[j] < ls[i].Name {
   233  			j++
   234  		}
   235  		if ls[i].Name == MetricName || (j < len(names) && ls[i].Name == names[j]) {
   236  			continue
   237  		}
   238  		ret = append(ret, ls[i])
   239  	}
   240  	return ret
   241  }
   242  
   243  // Copy returns a copy of the labels.
   244  func (ls Labels) Copy() Labels {
   245  	res := make(Labels, len(ls))
   246  	copy(res, ls)
   247  	return res
   248  }
   249  
   250  // Get returns the value for the label with the given name.
   251  // Returns an empty string if the label doesn't exist.
   252  func (ls Labels) Get(name string) string {
   253  	for _, l := range ls {
   254  		if l.Name == name {
   255  			return l.Value
   256  		}
   257  	}
   258  	return ""
   259  }
   260  
   261  // Has returns true if the label with the given name is present.
   262  func (ls Labels) Has(name string) bool {
   263  	for _, l := range ls {
   264  		if l.Name == name {
   265  			return true
   266  		}
   267  	}
   268  	return false
   269  }
   270  
   271  // HasDuplicateLabelNames returns whether ls has duplicate label names.
   272  // It assumes that the labelset is sorted.
   273  func (ls Labels) HasDuplicateLabelNames() (string, bool) {
   274  	for i, l := range ls {
   275  		if i == 0 {
   276  			continue
   277  		}
   278  		if l.Name == ls[i-1].Name {
   279  			return l.Name, true
   280  		}
   281  	}
   282  	return "", false
   283  }
   284  
   285  // WithoutEmpty returns the labelset without empty labels.
   286  // May return the same labelset.
   287  func (ls Labels) WithoutEmpty() Labels {
   288  	for _, v := range ls {
   289  		if v.Value != "" {
   290  			continue
   291  		}
   292  		// Do not copy the slice until it's necessary.
   293  		els := make(Labels, 0, len(ls)-1)
   294  		for _, v := range ls {
   295  			if v.Value != "" {
   296  				els = append(els, v)
   297  			}
   298  		}
   299  		return els
   300  	}
   301  	return ls
   302  }
   303  
   304  // Equal returns whether the two label sets are equal.
   305  func Equal(ls, o Labels) bool {
   306  	if len(ls) != len(o) {
   307  		return false
   308  	}
   309  	for i, l := range ls {
   310  		if l.Name != o[i].Name || l.Value != o[i].Value {
   311  			return false
   312  		}
   313  	}
   314  	return true
   315  }
   316  
   317  // Map returns a string map of the labels.
   318  func (ls Labels) Map() map[string]string {
   319  	m := make(map[string]string, len(ls))
   320  	for _, l := range ls {
   321  		m[l.Name] = l.Value
   322  	}
   323  	return m
   324  }
   325  
   326  // New returns a sorted Labels from the given labels.
   327  // The caller has to guarantee that all label names are unique.
   328  func New(ls ...Label) Labels {
   329  	set := make(Labels, 0, len(ls))
   330  	for _, l := range ls {
   331  		set = append(set, l)
   332  	}
   333  	sort.Sort(set)
   334  
   335  	return set
   336  }
   337  
   338  // FromMap returns new sorted Labels from the given map.
   339  func FromMap(m map[string]string) Labels {
   340  	l := make([]Label, 0, len(m))
   341  	for k, v := range m {
   342  		l = append(l, Label{Name: k, Value: v})
   343  	}
   344  	return New(l...)
   345  }
   346  
   347  // FromStrings creates new labels from pairs of strings.
   348  func FromStrings(ss ...string) Labels {
   349  	if len(ss)%2 != 0 {
   350  		panic("invalid number of strings")
   351  	}
   352  	var res Labels
   353  	for i := 0; i < len(ss); i += 2 {
   354  		res = append(res, Label{Name: ss[i], Value: ss[i+1]})
   355  	}
   356  
   357  	sort.Sort(res)
   358  	return res
   359  }
   360  
   361  // Compare compares the two label sets.
   362  // The result will be 0 if a==b, <0 if a < b, and >0 if a > b.
   363  func Compare(a, b Labels) int {
   364  	l := len(a)
   365  	if len(b) < l {
   366  		l = len(b)
   367  	}
   368  
   369  	for i := 0; i < l; i++ {
   370  		if a[i].Name != b[i].Name {
   371  			if a[i].Name < b[i].Name {
   372  				return -1
   373  			}
   374  			return 1
   375  		}
   376  		if a[i].Value != b[i].Value {
   377  			if a[i].Value < b[i].Value {
   378  				return -1
   379  			}
   380  			return 1
   381  		}
   382  	}
   383  	// If all labels so far were in common, the set with fewer labels comes first.
   384  	return len(a) - len(b)
   385  }
   386  
   387  // Builder allows modifying Labels.
   388  type Builder struct {
   389  	base Labels
   390  	del  []string
   391  	add  []Label
   392  }
   393  
   394  // NewBuilder returns a new LabelsBuilder.
   395  func NewBuilder(base Labels) *Builder {
   396  	b := &Builder{
   397  		del: make([]string, 0, 5),
   398  		add: make([]Label, 0, 5),
   399  	}
   400  	b.Reset(base)
   401  	return b
   402  }
   403  
   404  // Reset clears all current state for the builder.
   405  func (b *Builder) Reset(base Labels) {
   406  	b.base = base
   407  	b.del = b.del[:0]
   408  	b.add = b.add[:0]
   409  	for _, l := range b.base {
   410  		if l.Value == "" {
   411  			b.del = append(b.del, l.Name)
   412  		}
   413  	}
   414  }
   415  
   416  // Del deletes the label of the given name.
   417  func (b *Builder) Del(ns ...string) *Builder {
   418  	for _, n := range ns {
   419  		for i, a := range b.add {
   420  			if a.Name == n {
   421  				b.add = append(b.add[:i], b.add[i+1:]...)
   422  			}
   423  		}
   424  		b.del = append(b.del, n)
   425  	}
   426  	return b
   427  }
   428  
   429  // Set the name/value pair as a label.
   430  func (b *Builder) Set(n, v string) *Builder {
   431  	if v == "" {
   432  		// Empty labels are the same as missing labels.
   433  		return b.Del(n)
   434  	}
   435  	for i, a := range b.add {
   436  		if a.Name == n {
   437  			b.add[i].Value = v
   438  			return b
   439  		}
   440  	}
   441  	b.add = append(b.add, Label{Name: n, Value: v})
   442  
   443  	return b
   444  }
   445  
   446  // Labels returns the labels from the builder. If no modifications
   447  // were made, the original labels are returned.
   448  func (b *Builder) Labels() Labels {
   449  	if len(b.del) == 0 && len(b.add) == 0 {
   450  		return b.base
   451  	}
   452  
   453  	// In the general case, labels are removed, modified or moved
   454  	// rather than added.
   455  	res := make(Labels, 0, len(b.base))
   456  Outer:
   457  	for _, l := range b.base {
   458  		for _, n := range b.del {
   459  			if l.Name == n {
   460  				continue Outer
   461  			}
   462  		}
   463  		for _, la := range b.add {
   464  			if l.Name == la.Name {
   465  				continue Outer
   466  			}
   467  		}
   468  		res = append(res, l)
   469  	}
   470  	res = append(res, b.add...)
   471  	sort.Sort(res)
   472  
   473  	return res
   474  }