// LegacyDelete returns a handler that accepts delete requests. It deals with // the deprecated API. // // The returned handler is already instrumented for Prometheus. func LegacyDelete(ms storage.MetricStore) func(http.ResponseWriter, *http.Request, httprouter.Params) { var ps httprouter.Params var mtx sync.Mutex // Protects ps. instrumentedHandlerFunc := prometheus.InstrumentHandlerFunc( "delete", func(w http.ResponseWriter, _ *http.Request) { job := ps.ByName("job") instance := ps.ByName("instance") mtx.Unlock() if job == "" { http.Error(w, "job name is required", http.StatusBadRequest) return } labels := map[string]string{"job": job} if instance != "" { labels["instance"] = instance } ms.SubmitWriteRequest(storage.WriteRequest{ Labels: labels, Timestamp: time.Now(), }) w.WriteHeader(http.StatusAccepted) }, ) return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { mtx.Lock() ps = params instrumentedHandlerFunc(w, r) } }
// Delete returns a handler that accepts delete requests. // // The returned handler is already instrumented for Prometheus. func Delete(ms storage.MetricStore) func(http.ResponseWriter, *http.Request, httprouter.Params) { var ps httprouter.Params var mtx sync.Mutex // Protects ps. instrumentedHandlerFunc := prometheus.InstrumentHandlerFunc( "delete", func(w http.ResponseWriter, _ *http.Request) { job := ps.ByName("job") labelsString := ps.ByName("labels") mtx.Unlock() labels, err := splitLabels(labelsString) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if job == "" { http.Error(w, "job name is required", http.StatusBadRequest) return } labels["job"] = job ms.SubmitWriteRequest(storage.WriteRequest{ Labels: labels, Timestamp: time.Now(), }) w.WriteHeader(http.StatusAccepted) }, ) return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { mtx.Lock() ps = params instrumentedHandlerFunc(w, r) } }
// Status serves the status page. func Status( ms storage.MetricStore, assetFunc func(string) ([]byte, error), flags map[string]string, ) func(http.ResponseWriter, *http.Request) { birth := time.Now() return func(w http.ResponseWriter, _ *http.Request) { t := template.New("status") t.Funcs(template.FuncMap{ "value": func(f float64) string { return strconv.FormatFloat(f, 'f', -1, 64) }, }) tpl, err := assetFunc("template.html") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } _, err = t.Parse(string(tpl)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } buildInfo := map[string]string{ "version": version.Version, "revision": version.Revision, "branch": version.Branch, "buildUser": version.BuildUser, "buildDate": version.BuildDate, "goVersion": version.GoVersion, } d := &data{ MetricGroups: ms.GetMetricFamiliesMap(), Flags: flags, BuildInfo: buildInfo, Birth: birth, } err = t.Execute(w, d) if err != nil { // Hack to get a visible error message right at the top. fmt.Fprintf(w, `<div id="template-error" class="alert alert-danger">Error executing template: %s</div>`, html.EscapeString(err.Error())) fmt.Fprintln(w, `<script>$("#template-error").prependTo("body")</script>`) } } }
// Push returns an http.Handler which accepts samples over HTTP and stores them // in the MetricStore. If replace is true, all metrics for the job and instance // given by the request are deleted before new ones are stored. // // The returned handler is already instrumented for Prometheus. func Push( ms storage.MetricStore, replace bool, ) func(http.ResponseWriter, *http.Request, httprouter.Params) { var ps httprouter.Params var mtx sync.Mutex // Protects ps. instrumentedHandlerFunc := prometheus.InstrumentHandlerFunc( "push", func(w http.ResponseWriter, r *http.Request) { job := ps.ByName("job") labelsString := ps.ByName("labels") mtx.Unlock() labels, err := splitLabels(labelsString) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if job == "" { http.Error(w, "job name is required", http.StatusBadRequest) return } labels["job"] = job if replace { ms.SubmitWriteRequest(storage.WriteRequest{ Labels: labels, Timestamp: time.Now(), }) } var metricFamilies map[string]*dto.MetricFamily ctMediatype, ctParams, ctErr := mime.ParseMediaType(r.Header.Get("Content-Type")) if ctErr == nil && ctMediatype == "application/vnd.google.protobuf" && ctParams["encoding"] == "delimited" && ctParams["proto"] == "io.prometheus.client.MetricFamily" { metricFamilies = map[string]*dto.MetricFamily{} for { mf := &dto.MetricFamily{} if _, err = pbutil.ReadDelimited(r.Body, mf); err != nil { if err == io.EOF { err = nil } break } metricFamilies[mf.GetName()] = mf } } else { // We could do further content-type checks here, but the // fallback for now will anyway be the text format // version 0.0.4, so just go for it and see if it works. var parser text.Parser metricFamilies, err = parser.TextToMetricFamilies(r.Body) } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } sanitizeLabels(metricFamilies, labels) ms.SubmitWriteRequest(storage.WriteRequest{ Labels: labels, Timestamp: time.Now(), MetricFamilies: metricFamilies, }) w.WriteHeader(http.StatusAccepted) }, ) return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { mtx.Lock() ps = params instrumentedHandlerFunc(w, r) } }