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 }