// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "bytes" "encoding/json" "fmt" "slices" "sort" "strconv" ) // A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet // may be fully-qualified down to the point where it may resolve to a single // Metric in the data store or not. All operations that occur within the realm // of a LabelSet can emit a vector of Metric entities to which the LabelSet may // match. type LabelSet map[LabelName]LabelValue // Validate checks whether all names and values in the label set // are valid. func (ls LabelSet) Validate() error { for ln, lv := range ls { if !ln.IsValid() { return fmt.Errorf("invalid name %q", ln) } if !lv.IsValid() { return fmt.Errorf("invalid value %q", lv) } } return nil } // Equal returns true iff both label sets have exactly the same key/value pairs. func (ls LabelSet) Equal(o LabelSet) bool { if len(ls) != len(o) { return false } for ln, lv := range ls { olv, ok := o[ln] if !ok { return false } if olv != lv { return false } } return true } // Before compares the metrics, using the following criteria: // // If m has fewer labels than o, it is before o. If it has more, it is not. // // If the number of labels is the same, the superset of all label names is // sorted alphanumerically. The first differing label pair found in that order // determines the outcome: If the label does not exist at all in m, then m is // before o, and vice versa. Otherwise the label value is compared // alphanumerically. // // If m and o are equal, the method returns false. func (ls LabelSet) Before(o LabelSet) bool { if len(ls) < len(o) { return true } if len(ls) > len(o) { return false } lns := make(LabelNames, 0, len(ls)+len(o)) for ln := range ls { lns = append(lns, ln) } for ln := range o { lns = append(lns, ln) } // It's probably not worth it to de-dup lns. sort.Sort(lns) for _, ln := range lns { mlv, ok := ls[ln] if !ok { return true } olv, ok := o[ln] if !ok { return false } if mlv < olv { return true } if mlv > olv { return false } } return false } // Clone returns a copy of the label set. func (ls LabelSet) Clone() LabelSet { lsn := make(LabelSet, len(ls)) for ln, lv := range ls { lsn[ln] = lv } return lsn } // Merge is a helper function to non-destructively merge two label sets. func (l LabelSet) Merge(other LabelSet) LabelSet { result := make(LabelSet, len(l)) for k, v := range l { result[k] = v } for k, v := range other { result[k] = v } return result } // String will look like `{foo="bar", more="less"}`. Names are sorted alphabetically. func (l LabelSet) String() string { var lna [32]LabelName // On stack to avoid memory allocation for sorting names. labelNames := lna[:0] for name := range l { labelNames = append(labelNames, name) } slices.Sort(labelNames) var bytea [1024]byte // On stack to avoid memory allocation while building the output. b := bytes.NewBuffer(bytea[:0]) b.WriteByte('{') for i, name := range labelNames { if i > 0 { b.WriteString(", ") } b.WriteString(string(name)) b.WriteByte('=') b.Write(strconv.AppendQuote(b.AvailableBuffer(), string(l[name]))) } b.WriteByte('}') return b.String() } // Fingerprint returns the LabelSet's fingerprint. func (ls LabelSet) Fingerprint() Fingerprint { return labelSetToFingerprint(ls) } // FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing // algorithm, which is, however, more susceptible to hash collisions. func (ls LabelSet) FastFingerprint() Fingerprint { return labelSetToFastFingerprint(ls) } // UnmarshalJSON implements the json.Unmarshaler interface. func (l *LabelSet) UnmarshalJSON(b []byte) error { var m map[LabelName]LabelValue if err := json.Unmarshal(b, &m); err != nil { return err } // encoding/json only unmarshals maps of the form map[string]T. It treats // LabelName as a string and does not call its UnmarshalJSON method. // Thus, we have to replicate the behavior here. for ln := range m { if !ln.IsValid() { return fmt.Errorf("%q is not a valid label name", ln) } } *l = LabelSet(m) return nil }