1// Copyright 2018 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package push 15 16// This file contains only deprecated code. Remove after v0.9 is released. 17 18import ( 19 "bytes" 20 "fmt" 21 "io/ioutil" 22 "net/http" 23 "net/url" 24 "os" 25 "strings" 26 27 "github.com/prometheus/common/expfmt" 28 "github.com/prometheus/common/model" 29 30 "github.com/prometheus/client_golang/prometheus" 31) 32 33// FromGatherer triggers a metric collection by the provided Gatherer (which is 34// usually implemented by a prometheus.Registry) and pushes all gathered metrics 35// to the Pushgateway specified by url, using the provided job name and the 36// (optional) further grouping labels (the grouping map may be nil). See the 37// Pushgateway documentation for detailed implications of the job and other 38// grouping labels. Neither the job name nor any grouping label value may 39// contain a "/". The metrics pushed must not contain a job label of their own 40// nor any of the grouping labels. 41// 42// You can use just host:port or ip:port as url, in which case 'http://' is 43// added automatically. You can also include the schema in the URL. However, do 44// not include the '/metrics/jobs/...' part. 45// 46// Note that all previously pushed metrics with the same job and other grouping 47// labels will be replaced with the metrics pushed by this call. (It uses HTTP 48// method 'PUT' to push to the Pushgateway.) 49// 50// Deprecated: Please use a Pusher created with New instead. 51func FromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error { 52 return push(job, grouping, url, g, "PUT") 53} 54 55// AddFromGatherer works like FromGatherer, but only previously pushed metrics 56// with the same name (and the same job and other grouping labels) will be 57// replaced. (It uses HTTP method 'POST' to push to the Pushgateway.) 58// 59// Deprecated: Please use a Pusher created with New instead. 60func AddFromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error { 61 return push(job, grouping, url, g, "POST") 62} 63 64func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error { 65 if !strings.Contains(pushURL, "://") { 66 pushURL = "http://" + pushURL 67 } 68 if strings.HasSuffix(pushURL, "/") { 69 pushURL = pushURL[:len(pushURL)-1] 70 } 71 72 if strings.Contains(job, "/") { 73 return fmt.Errorf("job contains '/': %s", job) 74 } 75 urlComponents := []string{url.QueryEscape(job)} 76 for ln, lv := range grouping { 77 if !model.LabelName(ln).IsValid() { 78 return fmt.Errorf("grouping label has invalid name: %s", ln) 79 } 80 if strings.Contains(lv, "/") { 81 return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv) 82 } 83 urlComponents = append(urlComponents, ln, lv) 84 } 85 pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/")) 86 87 mfs, err := g.Gather() 88 if err != nil { 89 return err 90 } 91 buf := &bytes.Buffer{} 92 enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim) 93 // Check for pre-existing grouping labels: 94 for _, mf := range mfs { 95 for _, m := range mf.GetMetric() { 96 for _, l := range m.GetLabel() { 97 if l.GetName() == "job" { 98 return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m) 99 } 100 if _, ok := grouping[l.GetName()]; ok { 101 return fmt.Errorf( 102 "pushed metric %s (%s) already contains grouping label %s", 103 mf.GetName(), m, l.GetName(), 104 ) 105 } 106 } 107 } 108 enc.Encode(mf) 109 } 110 req, err := http.NewRequest(method, pushURL, buf) 111 if err != nil { 112 return err 113 } 114 req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim)) 115 resp, err := http.DefaultClient.Do(req) 116 if err != nil { 117 return err 118 } 119 defer resp.Body.Close() 120 if resp.StatusCode != 202 { 121 body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only. 122 return fmt.Errorf("unexpected status code %d while pushing to %s: %s", resp.StatusCode, pushURL, body) 123 } 124 return nil 125} 126 127// Collectors works like FromGatherer, but it does not use a Gatherer. Instead, 128// it collects from the provided collectors directly. It is a convenient way to 129// push only a few metrics. 130// 131// Deprecated: Please use a Pusher created with New instead. 132func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error { 133 return pushCollectors(job, grouping, url, "PUT", collectors...) 134} 135 136// AddCollectors works like AddFromGatherer, but it does not use a Gatherer. 137// Instead, it collects from the provided collectors directly. It is a 138// convenient way to push only a few metrics. 139// 140// Deprecated: Please use a Pusher created with New instead. 141func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error { 142 return pushCollectors(job, grouping, url, "POST", collectors...) 143} 144 145func pushCollectors(job string, grouping map[string]string, url, method string, collectors ...prometheus.Collector) error { 146 r := prometheus.NewRegistry() 147 for _, collector := range collectors { 148 if err := r.Register(collector); err != nil { 149 return err 150 } 151 } 152 return push(job, grouping, url, r, method) 153} 154 155// HostnameGroupingKey returns a label map with the only entry 156// {instance="<hostname>"}. This can be conveniently used as the grouping 157// parameter if metrics should be pushed with the hostname as label. The 158// returned map is created upon each call so that the caller is free to add more 159// labels to the map. 160// 161// Deprecated: Usually, metrics pushed to the Pushgateway should not be 162// host-centric. (You would use https://github.com/prometheus/node_exporter in 163// that case.) If you have the need to add the hostname to the grouping key, you 164// are probably doing something wrong. See 165// https://prometheus.io/docs/practices/pushing/ for details. 166func HostnameGroupingKey() map[string]string { 167 hostname, err := os.Hostname() 168 if err != nil { 169 return map[string]string{"instance": "unknown"} 170 } 171 return map[string]string{"instance": hostname} 172} 173