github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/scrape/model/labelset.go (about) 1 // Copyright 2013 The Prometheus Authors 2 // Copyright 2021 The Pyroscope Authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package model 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "sort" 22 "strings" 23 ) 24 25 // A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet 26 // may be fully-qualified down to the point where it may resolve to a single 27 // Metric in the data store or not. All operations that occur within the realm 28 // of a LabelSet can emit a vector of Metric entities to which the LabelSet may 29 // match. 30 type LabelSet map[LabelName]LabelValue 31 32 // Validate checks whether all names and values in the label set 33 // are valid. 34 func (ls LabelSet) Validate() error { 35 for ln, lv := range ls { 36 if !ln.IsValid() { 37 return fmt.Errorf("invalid name %q", ln) 38 } 39 if !lv.IsValid() { 40 return fmt.Errorf("invalid value %q", lv) 41 } 42 } 43 return nil 44 } 45 46 // Equal returns true iff both label sets have exactly the same key/value pairs. 47 func (ls LabelSet) Equal(o LabelSet) bool { 48 if len(ls) != len(o) { 49 return false 50 } 51 for ln, lv := range ls { 52 olv, ok := o[ln] 53 if !ok { 54 return false 55 } 56 if olv != lv { 57 return false 58 } 59 } 60 return true 61 } 62 63 // Before compares the metrics, using the following criteria: 64 // 65 // If m has fewer labels than o, it is before o. If it has more, it is not. 66 // 67 // If the number of labels is the same, the superset of all label names is 68 // sorted alphanumerically. The first differing label pair found in that order 69 // determines the outcome: If the label does not exist at all in m, then m is 70 // before o, and vice versa. Otherwise the label value is compared 71 // alphanumerically. 72 // 73 // If m and o are equal, the method returns false. 74 func (ls LabelSet) Before(o LabelSet) bool { 75 if len(ls) < len(o) { 76 return true 77 } 78 if len(ls) > len(o) { 79 return false 80 } 81 82 lns := make(LabelNames, 0, len(ls)+len(o)) 83 for ln := range ls { 84 lns = append(lns, ln) 85 } 86 for ln := range o { 87 lns = append(lns, ln) 88 } 89 // It's probably not worth it to de-dup lns. 90 sort.Sort(lns) 91 for _, ln := range lns { 92 mlv, ok := ls[ln] 93 if !ok { 94 return true 95 } 96 olv, ok := o[ln] 97 if !ok { 98 return false 99 } 100 if mlv < olv { 101 return true 102 } 103 if mlv > olv { 104 return false 105 } 106 } 107 return false 108 } 109 110 // Clone returns a copy of the label set. 111 func (ls LabelSet) Clone() LabelSet { 112 lsn := make(LabelSet, len(ls)) 113 for ln, lv := range ls { 114 lsn[ln] = lv 115 } 116 return lsn 117 } 118 119 // Merge is a helper function to non-destructively merge two label sets. 120 func (ls LabelSet) Merge(other LabelSet) LabelSet { 121 result := make(LabelSet, len(ls)) 122 123 for k, v := range ls { 124 result[k] = v 125 } 126 127 for k, v := range other { 128 result[k] = v 129 } 130 131 return result 132 } 133 134 func (ls LabelSet) String() string { 135 lstrs := make([]string, 0, len(ls)) 136 for l, v := range ls { 137 lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v)) 138 } 139 140 sort.Strings(lstrs) 141 return fmt.Sprintf("{%s}", strings.Join(lstrs, ", ")) 142 } 143 144 // UnmarshalJSON implements the json.Unmarshaler interface. 145 func (ls *LabelSet) UnmarshalJSON(b []byte) error { 146 var m map[LabelName]LabelValue 147 if err := json.Unmarshal(b, &m); err != nil { 148 return err 149 } 150 // encoding/json only unmarshals maps of the form map[string]T. It treats 151 // LabelName as a string and does not call its UnmarshalJSON method. 152 // Thus, we have to replicate the behavior here. 153 for ln := range m { 154 if !ln.IsValid() { 155 return fmt.Errorf("%q is not a valid label name", ln) 156 } 157 } 158 *ls = m 159 return nil 160 }