diff --git a/scrape/scrape.go b/scrape/scrape.go index e9d172a3e..10e9119a2 100644 --- a/scrape/scrape.go +++ b/scrape/scrape.go @@ -268,6 +268,7 @@ type scrapeLoopOptions struct { const maxAheadTime = 10 * time.Minute +// returning an empty label set is interpreted as "drop" type labelsMutator func(labels.Labels) labels.Labels func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, jitterSeed uint64, logger log.Logger, options *Options) (*scrapePool, error) { @@ -498,9 +499,9 @@ func (sp *scrapePool) Sync(tgs []*targetgroup.Group) { } targetSyncFailed.WithLabelValues(sp.config.JobName).Add(float64(len(failures))) for _, t := range targets { - if t.Labels().Len() > 0 { + if !t.Labels().IsEmpty() { all = append(all, t) - } else if t.DiscoveredLabels().Len() > 0 { + } else if !t.DiscoveredLabels().IsEmpty() { sp.droppedTargets = append(sp.droppedTargets, t) } } @@ -634,7 +635,7 @@ func verifyLabelLimits(lset labels.Labels, limits *labelLimits) error { met := lset.Get(labels.MetricName) if limits.labelLimit > 0 { - nbLabels := len(lset) + nbLabels := lset.Len() if nbLabels > int(limits.labelLimit) { return fmt.Errorf("label_limit exceeded (metric: %.50s, number of labels: %d, limit: %d)", met, nbLabels, limits.labelLimit) } @@ -644,7 +645,7 @@ func verifyLabelLimits(lset labels.Labels, limits *labelLimits) error { return nil } - for _, l := range lset { + return lset.Validate(func(l labels.Label) error { if limits.labelNameLengthLimit > 0 { nameLength := len(l.Name) if nameLength > int(limits.labelNameLengthLimit) { @@ -658,8 +659,8 @@ func verifyLabelLimits(lset labels.Labels, limits *labelLimits) error { return fmt.Errorf("label_value_length_limit exceeded (metric: %.50s, label name: %.50s, value: %.50q, length: %d, limit: %d)", met, l.Name, l.Value, valueLength, limits.labelValueLengthLimit) } } - } - return nil + return nil + }) } func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*relabel.Config) labels.Labels { @@ -667,37 +668,37 @@ func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*re targetLabels := target.Labels() if honor { - for _, l := range targetLabels { + targetLabels.Range(func(l labels.Label) { if !lset.Has(l.Name) { lb.Set(l.Name, l.Value) } - } + }) } else { - var conflictingExposedLabels labels.Labels - for _, l := range targetLabels { + var conflictingExposedLabels []labels.Label + targetLabels.Range(func(l labels.Label) { existingValue := lset.Get(l.Name) if existingValue != "" { conflictingExposedLabels = append(conflictingExposedLabels, labels.Label{Name: l.Name, Value: existingValue}) } // It is now safe to set the target label. lb.Set(l.Name, l.Value) - } + }) if len(conflictingExposedLabels) > 0 { resolveConflictingExposedLabels(lb, lset, targetLabels, conflictingExposedLabels) } } - res := lb.Labels(nil) + res := lb.Labels(labels.EmptyLabels()) if len(rc) > 0 { - res = relabel.Process(res, rc...) + res, _ = relabel.Process(res, rc...) } return res } -func resolveConflictingExposedLabels(lb *labels.Builder, exposedLabels, targetLabels, conflictingExposedLabels labels.Labels) { +func resolveConflictingExposedLabels(lb *labels.Builder, exposedLabels, targetLabels labels.Labels, conflictingExposedLabels []labels.Label) { sort.SliceStable(conflictingExposedLabels, func(i, j int) bool { return len(conflictingExposedLabels[i].Name) < len(conflictingExposedLabels[j].Name) }) @@ -708,7 +709,7 @@ func resolveConflictingExposedLabels(lb *labels.Builder, exposedLabels, targetLa newName = model.ExportedLabelPrefix + newName if !exposedLabels.Has(newName) && !targetLabels.Has(newName) && - !conflictingExposedLabels[:i].Has(newName) { + !labelSliceHas(conflictingExposedLabels[:i], newName) { conflictingExposedLabels[i].Name = newName break } @@ -720,15 +721,24 @@ func resolveConflictingExposedLabels(lb *labels.Builder, exposedLabels, targetLa } } +func labelSliceHas(lbls []labels.Label, name string) bool { + for _, l := range lbls { + if l.Name == name { + return true + } + } + return false +} + func mutateReportSampleLabels(lset labels.Labels, target *Target) labels.Labels { lb := labels.NewBuilder(lset) - for _, l := range target.Labels() { + target.Labels().Range(func(l labels.Label) { lb.Set(model.ExportedLabelPrefix+l.Name, lset.Get(l.Name)) lb.Set(l.Name, l.Value) - } + }) - return lb.Labels(nil) + return lb.Labels(labels.EmptyLabels()) } // appender returns an appender for ingested samples from the target. @@ -1599,8 +1609,8 @@ loop: // and relabeling and store the final label set. lset = sl.sampleMutator(lset) - // The label set may be set to nil to indicate dropping. - if lset == nil { + // The label set may be set to empty to indicate dropping. + if lset.IsEmpty() { sl.cache.addDropped(mets) continue } @@ -1857,12 +1867,10 @@ func (sl *scrapeLoop) addReportSample(app storage.Appender, s string, t int64, v ref = ce.ref lset = ce.lset } else { - lset = labels.Labels{ - // The constants are suffixed with the invalid \xff unicode rune to avoid collisions - // with scraped metrics in the cache. - // We have to drop it when building the actual metric. - labels.Label{Name: labels.MetricName, Value: s[:len(s)-1]}, - } + // The constants are suffixed with the invalid \xff unicode rune to avoid collisions + // with scraped metrics in the cache. + // We have to drop it when building the actual metric. + lset = labels.FromStrings(labels.MetricName, s[:len(s)-1]) lset = sl.reportSampleMutator(lset) } diff --git a/scrape/target.go b/scrape/target.go index f54623954..3f3afbd3a 100644 --- a/scrape/target.go +++ b/scrape/target.go @@ -172,22 +172,20 @@ func (t *Target) offset(interval time.Duration, jitterSeed uint64) time.Duration // Labels returns a copy of the set of all public labels of the target. func (t *Target) Labels() labels.Labels { - lset := make(labels.Labels, 0, len(t.labels)) - for _, l := range t.labels { + b := labels.NewScratchBuilder(t.labels.Len()) + t.labels.Range(func(l labels.Label) { if !strings.HasPrefix(l.Name, model.ReservedLabelPrefix) { - lset = append(lset, l) + b.Add(l.Name, l.Value) } - } - return lset + }) + return b.Labels() } // DiscoveredLabels returns a copy of the target's labels before any processing. func (t *Target) DiscoveredLabels() labels.Labels { t.mtx.Lock() defer t.mtx.Unlock() - lset := make(labels.Labels, len(t.discoveredLabels)) - copy(lset, t.discoveredLabels) - return lset + return t.discoveredLabels.Copy() } // SetDiscoveredLabels sets new DiscoveredLabels @@ -205,9 +203,9 @@ func (t *Target) URL() *url.URL { params[k] = make([]string, len(v)) copy(params[k], v) } - for _, l := range t.labels { + t.labels.Range(func(l labels.Label) { if !strings.HasPrefix(l.Name, model.ParamLabelPrefix) { - continue + return } ks := l.Name[len(model.ParamLabelPrefix):] @@ -216,7 +214,7 @@ func (t *Target) URL() *url.URL { } else { params[ks] = []string{l.Value} } - } + }) return &url.URL{ Scheme: t.labels.Get(model.SchemeLabel), @@ -374,15 +372,15 @@ func PopulateLabels(lset labels.Labels, cfg *config.ScrapeConfig, noDefaultPort } } - preRelabelLabels := lb.Labels(nil) - lset = relabel.Process(preRelabelLabels, cfg.RelabelConfigs...) + preRelabelLabels := lb.Labels(labels.EmptyLabels()) + lset, keep := relabel.Process(preRelabelLabels, cfg.RelabelConfigs...) // Check if the target was dropped. - if lset == nil { - return nil, preRelabelLabels, nil + if !keep { + return labels.EmptyLabels(), preRelabelLabels, nil } if v := lset.Get(model.AddressLabel); v == "" { - return nil, nil, errors.New("no address") + return labels.EmptyLabels(), labels.EmptyLabels(), errors.New("no address") } lb = labels.NewBuilder(lset) @@ -413,7 +411,7 @@ func PopulateLabels(lset labels.Labels, cfg *config.ScrapeConfig, noDefaultPort case "https": addr = addr + ":443" default: - return nil, nil, errors.Errorf("invalid scheme: %q", cfg.Scheme) + return labels.EmptyLabels(), labels.EmptyLabels(), errors.Errorf("invalid scheme: %q", cfg.Scheme) } lb.Set(model.AddressLabel, addr) } @@ -434,50 +432,54 @@ func PopulateLabels(lset labels.Labels, cfg *config.ScrapeConfig, noDefaultPort } if err := config.CheckTargetAddress(model.LabelValue(addr)); err != nil { - return nil, nil, err + return labels.EmptyLabels(), labels.EmptyLabels(), err } interval := lset.Get(model.ScrapeIntervalLabel) intervalDuration, err := model.ParseDuration(interval) if err != nil { - return nil, nil, errors.Errorf("error parsing scrape interval: %v", err) + return labels.EmptyLabels(), labels.EmptyLabels(), errors.Errorf("error parsing scrape interval: %v", err) } if time.Duration(intervalDuration) == 0 { - return nil, nil, errors.New("scrape interval cannot be 0") + return labels.EmptyLabels(), labels.EmptyLabels(), errors.New("scrape interval cannot be 0") } timeout := lset.Get(model.ScrapeTimeoutLabel) timeoutDuration, err := model.ParseDuration(timeout) if err != nil { - return nil, nil, errors.Errorf("error parsing scrape timeout: %v", err) + return labels.EmptyLabels(), labels.EmptyLabels(), errors.Errorf("error parsing scrape timeout: %v", err) } if time.Duration(timeoutDuration) == 0 { - return nil, nil, errors.New("scrape timeout cannot be 0") + return labels.EmptyLabels(), labels.EmptyLabels(), errors.New("scrape timeout cannot be 0") } if timeoutDuration > intervalDuration { - return nil, nil, errors.Errorf("scrape timeout cannot be greater than scrape interval (%q > %q)", timeout, interval) + return labels.EmptyLabels(), labels.EmptyLabels(), errors.Errorf("scrape timeout cannot be greater than scrape interval (%q > %q)", timeout, interval) } // Meta labels are deleted after relabelling. Other internal labels propagate to // the target which decides whether they will be part of their label set. - for _, l := range lset { + lset.Range(func(l labels.Label) { if strings.HasPrefix(l.Name, model.MetaLabelPrefix) { lb.Del(l.Name) } - } + }) // Default the instance label to the target address. if v := lset.Get(model.InstanceLabel); v == "" { lb.Set(model.InstanceLabel, addr) } - res = lb.Labels(nil) - for _, l := range res { + res = lb.Labels(labels.EmptyLabels()) + err = res.Validate(func(l labels.Label) error { // Check label values are valid, drop the target if not. if !model.LabelValue(l.Value).IsValid() { - return nil, nil, errors.Errorf("invalid label value for %q: %q", l.Name, l.Value) + return errors.Errorf("invalid label value for %q: %q", l.Name, l.Value) } + return nil + }) + if err != nil { + return labels.EmptyLabels(), labels.EmptyLabels(), err } return res, preRelabelLabels, nil } @@ -501,12 +503,12 @@ func TargetsFromGroup(tg *targetgroup.Group, cfg *config.ScrapeConfig, noDefault lset := labels.New(lbls...) - lbls, origLabels, err := PopulateLabels(lset, cfg, noDefaultPort) + lset, origLabels, err := PopulateLabels(lset, cfg, noDefaultPort) if err != nil { failures = append(failures, errors.Wrapf(err, "instance %d in group %s", i, tg)) } - if lbls != nil || origLabels != nil { - targets = append(targets, NewTarget(lbls, origLabels, cfg.Params)) + if !lset.IsEmpty() || !origLabels.IsEmpty() { + targets = append(targets, NewTarget(lset, origLabels, cfg.Params)) } } return targets, failures