github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/log/labels.go (about)

     1  package log
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/prometheus/prometheus/model/labels"
     7  
     8  	"github.com/grafana/loki/pkg/logqlmodel"
     9  )
    10  
    11  const MaxInternedStrings = 1024
    12  
    13  var EmptyLabelsResult = NewLabelsResult(labels.Labels{}, labels.Labels{}.Hash())
    14  
    15  // LabelsResult is a computed labels result that contains the labels set with associated string and hash.
    16  // The is mainly used for caching and returning labels computations out of pipelines and stages.
    17  type LabelsResult interface {
    18  	String() string
    19  	Labels() labels.Labels
    20  	Hash() uint64
    21  }
    22  
    23  // NewLabelsResult creates a new LabelsResult from a labels set and a hash.
    24  func NewLabelsResult(lbs labels.Labels, hash uint64) LabelsResult {
    25  	return &labelsResult{lbs: lbs, s: lbs.String(), h: hash}
    26  }
    27  
    28  type labelsResult struct {
    29  	lbs labels.Labels
    30  	s   string
    31  	h   uint64
    32  }
    33  
    34  func (l labelsResult) String() string {
    35  	return l.s
    36  }
    37  
    38  func (l labelsResult) Labels() labels.Labels {
    39  	return l.lbs
    40  }
    41  
    42  func (l labelsResult) Hash() uint64 {
    43  	return l.h
    44  }
    45  
    46  type hasher struct {
    47  	buf []byte // buffer for computing hash without bytes slice allocation.
    48  }
    49  
    50  // newHasher allow to compute hashes for labels by reusing the same buffer.
    51  func newHasher() *hasher {
    52  	return &hasher{
    53  		buf: make([]byte, 0, 1024),
    54  	}
    55  }
    56  
    57  // Hash hashes the labels
    58  func (h *hasher) Hash(lbs labels.Labels) uint64 {
    59  	var hash uint64
    60  	hash, h.buf = lbs.HashWithoutLabels(h.buf, []string(nil)...)
    61  	return hash
    62  }
    63  
    64  // BaseLabelsBuilder is a label builder used by pipeline and stages.
    65  // Only one base builder is used and it contains cache for each LabelsBuilders.
    66  type BaseLabelsBuilder struct {
    67  	del []string
    68  	add []labels.Label
    69  	// nolint:structcheck
    70  	// https://github.com/golangci/golangci-lint/issues/826
    71  	err string
    72  	// nolint:structcheck
    73  	errDetails string
    74  
    75  	groups            []string
    76  	parserKeyHints    ParserHint // label key hints for metric queries that allows to limit parser extractions to only this list of labels.
    77  	without, noLabels bool
    78  
    79  	resultCache map[uint64]LabelsResult
    80  	*hasher
    81  }
    82  
    83  // LabelsBuilder is the same as labels.Builder but tailored for this package.
    84  type LabelsBuilder struct {
    85  	base          labels.Labels
    86  	baseMap       map[string]string
    87  	buf           labels.Labels
    88  	currentResult LabelsResult
    89  	groupedResult LabelsResult
    90  
    91  	*BaseLabelsBuilder
    92  }
    93  
    94  // NewBaseLabelsBuilderWithGrouping creates a new base labels builder with grouping to compute results.
    95  func NewBaseLabelsBuilderWithGrouping(groups []string, parserKeyHints ParserHint, without, noLabels bool) *BaseLabelsBuilder {
    96  	return &BaseLabelsBuilder{
    97  		del:            make([]string, 0, 5),
    98  		add:            make([]labels.Label, 0, 16),
    99  		resultCache:    make(map[uint64]LabelsResult),
   100  		hasher:         newHasher(),
   101  		groups:         groups,
   102  		parserKeyHints: parserKeyHints,
   103  		noLabels:       noLabels,
   104  		without:        without,
   105  	}
   106  }
   107  
   108  // NewLabelsBuilder creates a new base labels builder.
   109  func NewBaseLabelsBuilder() *BaseLabelsBuilder {
   110  	return NewBaseLabelsBuilderWithGrouping(nil, noParserHints, false, false)
   111  }
   112  
   113  // ForLabels creates a labels builder for a given labels set as base.
   114  // The labels cache is shared across all created LabelsBuilders.
   115  func (b *BaseLabelsBuilder) ForLabels(lbs labels.Labels, hash uint64) *LabelsBuilder {
   116  	if labelResult, ok := b.resultCache[hash]; ok {
   117  		res := &LabelsBuilder{
   118  			base:              lbs,
   119  			currentResult:     labelResult,
   120  			BaseLabelsBuilder: b,
   121  		}
   122  		return res
   123  	}
   124  	labelResult := NewLabelsResult(lbs, hash)
   125  	b.resultCache[hash] = labelResult
   126  	res := &LabelsBuilder{
   127  		base:              lbs,
   128  		currentResult:     labelResult,
   129  		BaseLabelsBuilder: b,
   130  	}
   131  	return res
   132  }
   133  
   134  // Reset clears all current state for the builder.
   135  func (b *LabelsBuilder) Reset() {
   136  	b.del = b.del[:0]
   137  	b.add = b.add[:0]
   138  	b.err = ""
   139  	b.errDetails = ""
   140  }
   141  
   142  // ParserLabelHints returns a limited list of expected labels to extract for metric queries.
   143  // Returns nil when it's impossible to hint labels extractions.
   144  func (b *BaseLabelsBuilder) ParserLabelHints() ParserHint {
   145  	return b.parserKeyHints
   146  }
   147  
   148  // SetErr sets the error label.
   149  func (b *LabelsBuilder) SetErr(err string) *LabelsBuilder {
   150  	b.err = err
   151  	return b
   152  }
   153  
   154  // GetErr return the current error label value.
   155  func (b *LabelsBuilder) GetErr() string {
   156  	return b.err
   157  }
   158  
   159  // HasErr tells if the error label has been set.
   160  func (b *LabelsBuilder) HasErr() bool {
   161  	return b.err != ""
   162  }
   163  
   164  func (b *LabelsBuilder) SetErrorDetails(desc string) *LabelsBuilder {
   165  	b.errDetails = desc
   166  	return b
   167  }
   168  
   169  func (b *LabelsBuilder) GetErrorDetails() string {
   170  	return b.errDetails
   171  }
   172  
   173  func (b *LabelsBuilder) HasErrorDetails() bool {
   174  	return b.errDetails != ""
   175  }
   176  
   177  // BaseHas returns the base labels have the given key
   178  func (b *LabelsBuilder) BaseHas(key string) bool {
   179  	return b.base.Has(key)
   180  }
   181  
   182  // Get returns the value of a labels key if it exists.
   183  func (b *LabelsBuilder) Get(key string) (string, bool) {
   184  	for _, a := range b.add {
   185  		if a.Name == key {
   186  			return a.Value, true
   187  		}
   188  	}
   189  	for _, d := range b.del {
   190  		if d == key {
   191  			return "", false
   192  		}
   193  	}
   194  
   195  	for _, l := range b.base {
   196  		if l.Name == key {
   197  			return l.Value, true
   198  		}
   199  	}
   200  	return "", false
   201  }
   202  
   203  // Del deletes the label of the given name.
   204  func (b *LabelsBuilder) Del(ns ...string) *LabelsBuilder {
   205  	for _, n := range ns {
   206  		for i, a := range b.add {
   207  			if a.Name == n {
   208  				b.add = append(b.add[:i], b.add[i+1:]...)
   209  			}
   210  		}
   211  		b.del = append(b.del, n)
   212  	}
   213  	return b
   214  }
   215  
   216  // Set the name/value pair as a label.
   217  func (b *LabelsBuilder) Set(n, v string) *LabelsBuilder {
   218  	for i, a := range b.add {
   219  		if a.Name == n {
   220  			b.add[i].Value = v
   221  			return b
   222  		}
   223  	}
   224  	b.add = append(b.add, labels.Label{Name: n, Value: v})
   225  
   226  	return b
   227  }
   228  
   229  // Labels returns the labels from the builder. If no modifications
   230  // were made, the original labels are returned.
   231  func (b *LabelsBuilder) labels() labels.Labels {
   232  	b.buf = b.unsortedLabels(b.buf)
   233  	sort.Sort(b.buf)
   234  	return b.buf
   235  }
   236  
   237  func (b *LabelsBuilder) unsortedLabels(buf labels.Labels) labels.Labels {
   238  	if len(b.del) == 0 && len(b.add) == 0 {
   239  		if buf == nil {
   240  			buf = make(labels.Labels, 0, len(b.base)+1)
   241  		} else {
   242  			buf = buf[:0]
   243  		}
   244  		buf = append(buf, b.base...)
   245  		if b.err != "" {
   246  			buf = append(buf, labels.Label{Name: logqlmodel.ErrorLabel, Value: b.err})
   247  		}
   248  		if b.errDetails != "" {
   249  			buf = append(buf, labels.Label{Name: logqlmodel.ErrorDetailsLabel, Value: b.errDetails})
   250  		}
   251  		return buf
   252  	}
   253  
   254  	// In the general case, labels are removed, modified or moved
   255  	// rather than added.
   256  	if buf == nil {
   257  		buf = make(labels.Labels, 0, len(b.base)+len(b.add)+1)
   258  	} else {
   259  		buf = buf[:0]
   260  	}
   261  Outer:
   262  	for _, l := range b.base {
   263  		for _, n := range b.del {
   264  			if l.Name == n {
   265  				continue Outer
   266  			}
   267  		}
   268  		for _, la := range b.add {
   269  			if l.Name == la.Name {
   270  				continue Outer
   271  			}
   272  		}
   273  		buf = append(buf, l)
   274  	}
   275  	buf = append(buf, b.add...)
   276  	if b.err != "" {
   277  		buf = append(buf, labels.Label{Name: logqlmodel.ErrorLabel, Value: b.err})
   278  	}
   279  
   280  	return buf
   281  }
   282  
   283  func (b *LabelsBuilder) Map() map[string]string {
   284  	if len(b.del) == 0 && len(b.add) == 0 && b.err == "" {
   285  		if b.baseMap == nil {
   286  			b.baseMap = b.base.Map()
   287  		}
   288  		return b.baseMap
   289  	}
   290  	b.buf = b.unsortedLabels(b.buf)
   291  	// todo should we also cache maps since limited by the result ?
   292  	// Maps also don't create a copy of the labels.
   293  	res := make(map[string]string, len(b.buf))
   294  	for _, l := range b.buf {
   295  		res[l.Name] = l.Value
   296  	}
   297  	return res
   298  }
   299  
   300  // LabelsResult returns the LabelsResult from the builder.
   301  // No grouping is applied and the cache is used when possible.
   302  func (b *LabelsBuilder) LabelsResult() LabelsResult {
   303  	// unchanged path.
   304  	if len(b.del) == 0 && len(b.add) == 0 && b.err == "" {
   305  		return b.currentResult
   306  	}
   307  	return b.toResult(b.labels())
   308  }
   309  
   310  func (b *BaseLabelsBuilder) toResult(buf labels.Labels) LabelsResult {
   311  	hash := b.hasher.Hash(buf)
   312  	if cached, ok := b.resultCache[hash]; ok {
   313  		return cached
   314  	}
   315  	res := NewLabelsResult(buf.Copy(), hash)
   316  	b.resultCache[hash] = res
   317  	return res
   318  }
   319  
   320  // GroupedLabels returns the LabelsResult from the builder.
   321  // Groups are applied and the cache is used when possible.
   322  func (b *LabelsBuilder) GroupedLabels() LabelsResult {
   323  	if b.err != "" {
   324  		// We need to return now before applying grouping otherwise the error might get lost.
   325  		return b.LabelsResult()
   326  	}
   327  	if b.noLabels {
   328  		return EmptyLabelsResult
   329  	}
   330  	// unchanged path.
   331  	if len(b.del) == 0 && len(b.add) == 0 {
   332  		if len(b.groups) == 0 {
   333  			return b.currentResult
   334  		}
   335  		return b.toBaseGroup()
   336  	}
   337  	// no grouping
   338  	if len(b.groups) == 0 {
   339  		return b.LabelsResult()
   340  	}
   341  
   342  	if b.without {
   343  		return b.withoutResult()
   344  	}
   345  	return b.withResult()
   346  }
   347  
   348  func (b *LabelsBuilder) withResult() LabelsResult {
   349  	if b.buf == nil {
   350  		b.buf = make(labels.Labels, 0, len(b.groups))
   351  	} else {
   352  		b.buf = b.buf[:0]
   353  	}
   354  Outer:
   355  	for _, g := range b.groups {
   356  		for _, n := range b.del {
   357  			if g == n {
   358  				continue Outer
   359  			}
   360  		}
   361  		for _, la := range b.add {
   362  			if g == la.Name {
   363  				b.buf = append(b.buf, la)
   364  				continue Outer
   365  			}
   366  		}
   367  		for _, l := range b.base {
   368  			if g == l.Name {
   369  				b.buf = append(b.buf, l)
   370  				continue Outer
   371  			}
   372  		}
   373  	}
   374  	return b.toResult(b.buf)
   375  }
   376  
   377  func (b *LabelsBuilder) withoutResult() LabelsResult {
   378  	if b.buf == nil {
   379  		size := len(b.base) + len(b.add) - len(b.del) - len(b.groups)
   380  		if size < 0 {
   381  			size = 0
   382  		}
   383  		b.buf = make(labels.Labels, 0, size)
   384  	} else {
   385  		b.buf = b.buf[:0]
   386  	}
   387  Outer:
   388  	for _, l := range b.base {
   389  		for _, n := range b.del {
   390  			if l.Name == n {
   391  				continue Outer
   392  			}
   393  		}
   394  		for _, la := range b.add {
   395  			if l.Name == la.Name {
   396  				continue Outer
   397  			}
   398  		}
   399  		for _, lg := range b.groups {
   400  			if l.Name == lg {
   401  				continue Outer
   402  			}
   403  		}
   404  		b.buf = append(b.buf, l)
   405  	}
   406  OuterAdd:
   407  	for _, la := range b.add {
   408  		for _, lg := range b.groups {
   409  			if la.Name == lg {
   410  				continue OuterAdd
   411  			}
   412  		}
   413  		b.buf = append(b.buf, la)
   414  	}
   415  	sort.Sort(b.buf)
   416  	return b.toResult(b.buf)
   417  }
   418  
   419  func (b *LabelsBuilder) toBaseGroup() LabelsResult {
   420  	if b.groupedResult != nil {
   421  		return b.groupedResult
   422  	}
   423  	var lbs labels.Labels
   424  	if b.without {
   425  		lbs = labels.NewBuilder(b.base).Del(b.groups...).Labels()
   426  	} else {
   427  		lbs = labels.NewBuilder(b.base).Keep(b.groups...).Labels()
   428  	}
   429  	res := NewLabelsResult(lbs, lbs.Hash())
   430  	b.groupedResult = res
   431  	return res
   432  }
   433  
   434  type internedStringSet map[string]struct {
   435  	s  string
   436  	ok bool
   437  }
   438  
   439  func (i internedStringSet) Get(data []byte, createNew func() (string, bool)) (string, bool) {
   440  	s, ok := i[string(data)]
   441  	if ok {
   442  		return s.s, s.ok
   443  	}
   444  	new, ok := createNew()
   445  	if len(i) >= MaxInternedStrings {
   446  		return new, ok
   447  	}
   448  	i[string(data)] = struct {
   449  		s  string
   450  		ok bool
   451  	}{s: new, ok: ok}
   452  	return new, ok
   453  }