// 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) } }
// 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) } }
// 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) } }