2026-01-05 07:46:21 -05:00
// Copyright The Prometheus Authors
2023-11-17 13:19:40 -05:00
// 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 labels
import (
"bytes"
"encoding/json"
2024-01-15 11:24:46 -05:00
"slices"
2023-11-17 13:19:40 -05:00
"strconv"
2024-06-18 05:57:37 -04:00
"unsafe"
2023-11-17 13:19:40 -05:00
"github.com/prometheus/common/model"
)
const (
2025-05-17 05:37:25 -04:00
// MetricName is a special label name that represent a metric name.
2025-11-05 07:47:34 -05:00
//
2025-11-24 03:31:28 -05:00
// Deprecated: Instead, consider using schema.Metadata structure and its methods for consistent metadata behaviour with the newly added __type__ and __unit__ labels. Alternatively use github.com/prometheus/common/model.MetricNameLabel for the direct replacement.
//
// labels package is providing label transport, agnostic to semantic meaning of each label.
2025-05-17 05:37:25 -04:00
MetricName = "__name__"
AlertName = "alertname"
BucketLabel = "le"
2023-11-17 13:19:40 -05:00
2024-07-15 04:47:16 -04:00
labelSep = '\xfe' // Used at beginning of `Bytes` return.
sep = '\xff' // Used between labels in `Bytes` and `Hash`.
2023-11-17 13:19:40 -05:00
)
2024-07-15 04:47:16 -04:00
var seps = [ ] byte { sep } // Used with Hash, which has no WriteByte method.
2023-11-17 13:19:40 -05:00
2025-05-17 05:37:25 -04:00
// Label is a key/value a pair of strings.
2023-11-17 13:19:40 -05:00
type Label struct {
Name , Value string
}
func ( ls Labels ) String ( ) string {
2025-09-08 11:26:41 -04:00
return ls . stringImpl ( true )
}
// StringNoSpace is like String but does not add a space after commas.
func ( ls Labels ) StringNoSpace ( ) string {
return ls . stringImpl ( false )
}
func ( ls Labels ) stringImpl ( addSpace bool ) string {
2024-03-12 07:34:03 -04:00
var bytea [ 1024 ] byte // On stack to avoid memory allocation while building the output.
b := bytes . NewBuffer ( bytea [ : 0 ] )
2023-11-17 13:19:40 -05:00
b . WriteByte ( '{' )
i := 0
ls . Range ( func ( l Label ) {
if i > 0 {
b . WriteByte ( ',' )
2025-09-08 11:26:41 -04:00
if addSpace {
b . WriteByte ( ' ' )
}
2023-11-17 13:19:40 -05:00
}
2025-08-18 04:09:00 -04:00
if ! model . LegacyValidation . IsValidLabelName ( l . Name ) {
2024-12-03 12:33:10 -05:00
b . Write ( strconv . AppendQuote ( b . AvailableBuffer ( ) , l . Name ) )
} else {
b . WriteString ( l . Name )
}
2023-11-17 13:19:40 -05:00
b . WriteByte ( '=' )
2024-03-12 07:34:03 -04:00
b . Write ( strconv . AppendQuote ( b . AvailableBuffer ( ) , l . Value ) )
2023-11-17 13:19:40 -05:00
i ++
} )
b . WriteByte ( '}' )
return b . String ( )
}
// MarshalJSON implements json.Marshaler.
func ( ls Labels ) MarshalJSON ( ) ( [ ] byte , error ) {
return json . Marshal ( ls . Map ( ) )
}
// UnmarshalJSON implements json.Unmarshaler.
func ( ls * Labels ) UnmarshalJSON ( b [ ] byte ) error {
var m map [ string ] string
if err := json . Unmarshal ( b , & m ) ; err != nil {
return err
}
* ls = FromMap ( m )
return nil
}
// MarshalYAML implements yaml.Marshaler.
2025-08-27 08:38:54 -04:00
func ( ls Labels ) MarshalYAML ( ) ( any , error ) {
2023-11-17 13:19:40 -05:00
return ls . Map ( ) , nil
}
// UnmarshalYAML implements yaml.Unmarshaler.
2025-08-27 08:38:54 -04:00
func ( ls * Labels ) UnmarshalYAML ( unmarshal func ( any ) error ) error {
2023-11-17 13:19:40 -05:00
var m map [ string ] string
if err := unmarshal ( & m ) ; err != nil {
return err
}
* ls = FromMap ( m )
return nil
}
// IsValid checks if the metric name or label names are valid.
2024-08-28 11:15:42 -04:00
func ( ls Labels ) IsValid ( validationScheme model . ValidationScheme ) bool {
2023-11-17 13:19:40 -05:00
err := ls . Validate ( func ( l Label ) error {
2024-08-28 11:15:42 -04:00
if l . Name == model . MetricNameLabel {
// If the default validation scheme has been overridden with legacy mode,
// we need to call the special legacy validation checker.
2025-08-18 04:09:00 -04:00
if ! validationScheme . IsValidMetricName ( l . Value ) {
2024-08-28 11:15:42 -04:00
return strconv . ErrSyntax
}
2023-11-17 13:19:40 -05:00
}
2025-08-18 04:09:00 -04:00
if ! validationScheme . IsValidLabelName ( l . Name ) || ! model . LabelValue ( l . Value ) . IsValid ( ) {
2023-11-17 13:19:40 -05:00
return strconv . ErrSyntax
}
return nil
} )
return err == nil
}
// Map returns a string map of the labels.
func ( ls Labels ) Map ( ) map [ string ] string {
m := make ( map [ string ] string )
ls . Range ( func ( l Label ) {
m [ l . Name ] = l . Value
} )
return m
}
// FromMap returns new sorted Labels from the given map.
func FromMap ( m map [ string ] string ) Labels {
l := make ( [ ] Label , 0 , len ( m ) )
for k , v := range m {
l = append ( l , Label { Name : k , Value : v } )
}
return New ( l ... )
}
// NewBuilder returns a new LabelsBuilder.
func NewBuilder ( base Labels ) * Builder {
b := & Builder {
del : make ( [ ] string , 0 , 5 ) ,
add : make ( [ ] Label , 0 , 5 ) ,
}
b . Reset ( base )
return b
}
// Del deletes the label of the given name.
func ( b * Builder ) Del ( ns ... string ) * Builder {
for _ , n := range ns {
for i , a := range b . add {
if a . Name == n {
b . add = append ( b . add [ : i ] , b . add [ i + 1 : ] ... )
}
}
b . del = append ( b . del , n )
}
return b
}
// Keep removes all labels from the base except those with the given names.
func ( b * Builder ) Keep ( ns ... string ) * Builder {
b . base . Range ( func ( l Label ) {
2025-04-29 22:43:26 -04:00
if slices . Contains ( ns , l . Name ) {
return
2023-11-17 13:19:40 -05:00
}
b . del = append ( b . del , l . Name )
} )
return b
}
// Set the name/value pair as a label. A value of "" means delete that label.
func ( b * Builder ) Set ( n , v string ) * Builder {
if v == "" {
// Empty labels are the same as missing labels.
return b . Del ( n )
}
for i , a := range b . add {
if a . Name == n {
b . add [ i ] . Value = v
return b
}
}
b . add = append ( b . add , Label { Name : n , Value : v } )
return b
}
func ( b * Builder ) Get ( n string ) string {
// Del() removes entries from .add but Set() does not remove from .del, so check .add first.
for _ , a := range b . add {
if a . Name == n {
return a . Value
}
}
if slices . Contains ( b . del , n ) {
return ""
}
return b . base . Get ( n )
}
// Range calls f on each label in the Builder.
func ( b * Builder ) Range ( f func ( l Label ) ) {
// Stack-based arrays to avoid heap allocation in most cases.
var addStack [ 128 ] Label
var delStack [ 128 ] string
// Take a copy of add and del, so they are unaffected by calls to Set() or Del().
origAdd , origDel := append ( addStack [ : 0 ] , b . add ... ) , append ( delStack [ : 0 ] , b . del ... )
b . base . Range ( func ( l Label ) {
if ! slices . Contains ( origDel , l . Name ) && ! contains ( origAdd , l . Name ) {
f ( l )
}
} )
for _ , a := range origAdd {
f ( a )
}
}
func contains ( s [ ] Label , n string ) bool {
for _ , a := range s {
if a . Name == n {
return true
}
}
return false
}
2024-06-18 05:57:37 -04:00
func yoloString ( b [ ] byte ) string {
2024-09-21 06:19:21 -04:00
return unsafe . String ( unsafe . SliceData ( b ) , len ( b ) )
2024-06-18 05:57:37 -04:00
}