diff --git a/retrieval/target.go b/retrieval/target.go index 8ad2d8f0c..aae723789 100644 --- a/retrieval/target.go +++ b/retrieval/target.go @@ -19,7 +19,6 @@ import ( "math/rand" "net/http" "net/url" - "os" "strings" "sync" "time" @@ -53,8 +52,6 @@ const ( var ( errIngestChannelFull = errors.New("ingestion channel full") - localhostRepresentations = []string{"127.0.0.1", "localhost"} - targetIntervalLength = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Namespace: namespace, @@ -118,17 +115,11 @@ type Target interface { URL() string // Used to populate the `instance` label in metrics. InstanceIdentifier() string - // The URL as seen from other hosts. References to localhost are resolved - // to the address of the prometheus server. - GlobalURL() string // Return the labels describing the targets. These are the base labels // as well as internal labels. - Labels() clientmodel.LabelSet + fullLabels() clientmodel.LabelSet // Return the target's base labels. BaseLabels() clientmodel.LabelSet - // Return the target's base labels without job and instance label. That's - // useful for display purposes. - BaseLabelsWithoutJobAndInstance() clientmodel.LabelSet // Start scraping the target in regular intervals. RunScraper(storage.SampleAppender) // Stop scraping, synchronous. @@ -195,7 +186,6 @@ type target struct { // The status object for the target. It is only set once on initialization. status *TargetStatus - // The HTTP client used to scrape the target's endpoint. httpClient *http.Client @@ -419,66 +409,43 @@ func (t *target) InstanceIdentifier() string { return t.url.Host } -// GlobalURL implements Target. -func (t *target) GlobalURL() string { - url := t.URL() - - hostname, err := os.Hostname() - if err != nil { - glog.Warningf("Couldn't get hostname: %s, returning target.URL()", err) - return url - } - for _, localhostRepresentation := range localhostRepresentations { - url = strings.Replace(url, "//"+localhostRepresentation, "//"+hostname, 1) - } - return url -} - -// Labels implements Target. -func (t *target) Labels() clientmodel.LabelSet { +// fullLabels implements Target. +func (t *target) fullLabels() clientmodel.LabelSet { t.RLock() defer t.RUnlock() - ls := clientmodel.LabelSet{} + lset := make(clientmodel.LabelSet, len(t.baseLabels)+2) for ln, lv := range t.baseLabels { - ls[ln] = lv + lset[ln] = lv } - ls[clientmodel.MetricsPathLabel] = clientmodel.LabelValue(t.url.Path) - ls[clientmodel.AddressLabel] = clientmodel.LabelValue(t.url.Host) - return ls + lset[clientmodel.MetricsPathLabel] = clientmodel.LabelValue(t.url.Path) + lset[clientmodel.AddressLabel] = clientmodel.LabelValue(t.url.Host) + return lset } // BaseLabels implements Target. func (t *target) BaseLabels() clientmodel.LabelSet { t.RLock() defer t.RUnlock() - return t.baseLabels -} - -// BaseLabelsWithoutJobAndInstance implements Target. -// -// TODO(fabxc): This method does not have to be part of the interface. Implement this -// as a template filter func for the single use case. -func (t *target) BaseLabelsWithoutJobAndInstance() clientmodel.LabelSet { - t.RLock() - defer t.RUnlock() - ls := clientmodel.LabelSet{} + lset := make(clientmodel.LabelSet, len(t.baseLabels)) for ln, lv := range t.baseLabels { - if ln != clientmodel.JobLabel && ln != clientmodel.InstanceLabel { - ls[ln] = lv - } + lset[ln] = lv } - return ls + return lset } func (t *target) recordScrapeHealth(sampleAppender storage.SampleAppender, timestamp clientmodel.Timestamp, scrapeDuration time.Duration) { - healthMetric := clientmodel.Metric{} - durationMetric := clientmodel.Metric{} - for label, value := range t.BaseLabels() { + t.RLock() + healthMetric := make(clientmodel.Metric, len(t.baseLabels)+1) + durationMetric := make(clientmodel.Metric, len(t.baseLabels)+1) + + healthMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeHealthMetricName) + durationMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeDurationMetricName) + + for label, value := range t.baseLabels { healthMetric[label] = value durationMetric[label] = value } - healthMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeHealthMetricName) - durationMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeDurationMetricName) + t.RUnlock() healthValue := clientmodel.SampleValue(0) if t.status.State() == Healthy { diff --git a/retrieval/target_test.go b/retrieval/target_test.go index 7d2c1327d..a70837791 100644 --- a/retrieval/target_test.go +++ b/retrieval/target_test.go @@ -44,13 +44,6 @@ func TestBaseLabels(t *testing.T) { if !reflect.DeepEqual(want, got) { t.Errorf("want base labels %v, got %v", want, got) } - delete(want, clientmodel.JobLabel) - delete(want, clientmodel.InstanceLabel) - - got = target.BaseLabelsWithoutJobAndInstance() - if !reflect.DeepEqual(want, got) { - t.Errorf("want base labels %v, got %v", want, got) - } } func TestTargetScrapeUpdatesState(t *testing.T) { diff --git a/retrieval/targetmanager.go b/retrieval/targetmanager.go index cacc2126e..8b71790bc 100644 --- a/retrieval/targetmanager.go +++ b/retrieval/targetmanager.go @@ -215,7 +215,7 @@ func (tm *TargetManager) updateTargetGroup(tgroup *config.TargetGroup, cfg *conf // to build up. wg.Add(1) go func(t Target) { - match.Update(cfg, t.Labels()) + match.Update(cfg, t.fullLabels()) wg.Done() }(tnew) newTargets[i] = match diff --git a/web/templates/status.html b/web/templates/status.html index 900470978..ba37f458b 100644 --- a/web/templates/status.html +++ b/web/templates/status.html @@ -48,7 +48,7 @@ {{range $pool}} - {{.URL}} + {{.URL}} @@ -56,7 +56,7 @@ - {{.BaseLabelsWithoutJobAndInstance}} + {{stripLabels .BaseLabels "job" "instance"}} {{if .Status.LastScrape.IsZero}}Never{{else}}{{since .Status.LastScrape}} ago{{end}} diff --git a/web/web.go b/web/web.go index bd53b0de5..1548046c3 100644 --- a/web/web.go +++ b/web/web.go @@ -29,10 +29,14 @@ import ( "github.com/golang/glog" "github.com/prometheus/client_golang/prometheus" + clientmodel "github.com/prometheus/client_golang/model" + "github.com/prometheus/prometheus/web/api" "github.com/prometheus/prometheus/web/blob" ) +var localhostRepresentations = []string{"127.0.0.1", "localhost"} + // Commandline flags. var ( listenAddress = flag.String("web.listen-address", ":9090", "Address to listen on for the web interface, API, and telemetry.") @@ -150,28 +154,53 @@ func getConsoles(pathPrefix string) string { return "" } -func getTemplate(name string, pathPrefix string) (t *template.Template, err error) { - t = template.New("_base") +func getTemplate(name string, pathPrefix string) (*template.Template, error) { + t := template.New("_base") + var err error t.Funcs(template.FuncMap{ "since": time.Since, "getConsoles": func() string { return getConsoles(pathPrefix) }, "pathPrefix": func() string { return pathPrefix }, + "stripLabels": func(lset clientmodel.LabelSet, labels ...clientmodel.LabelName) clientmodel.LabelSet { + for _, ln := range labels { + delete(lset, ln) + } + return lset + }, + "globalURL": func(url string) string { + hostname, err := os.Hostname() + if err != nil { + glog.Warningf("Couldn't get hostname: %s, returning target.URL()", err) + return url + } + for _, localhostRepresentation := range localhostRepresentations { + url = strings.Replace(url, "//"+localhostRepresentation, "//"+hostname, 1) + } + return url + }, }) + file, err := getTemplateFile("_base") if err != nil { - glog.Error("Could not read base template: ", err) + glog.Errorln("Could not read base template:", err) return nil, err } - t.Parse(file) + t, err = t.Parse(file) + if err != nil { + glog.Errorln("Could not parse base template:", err) + } file, err = getTemplateFile(name) if err != nil { - glog.Error("Could not read base template: ", err) + glog.Error("Could not read template %d: ", name, err) return nil, err } - t.Parse(file) - return + t, err = t.Parse(file) + if err != nil { + glog.Errorf("Could not parse template %s: %s", name, err) + } + return t, err } func executeTemplate(w http.ResponseWriter, name string, data interface{}, pathPrefix string) {