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. If check is true, the pushed metrics are immediately checked for consistency (with ex
( ms storage.MetricStore, replace, check, jobBase64Encoded bool, logger *slog.Logger, )
| 57 | // |
| 58 | // The returned handler is already instrumented for Prometheus. |
| 59 | func Push( |
| 60 | ms storage.MetricStore, |
| 61 | replace, check, jobBase64Encoded bool, |
| 62 | logger *slog.Logger, |
| 63 | ) func(http.ResponseWriter, *http.Request) { |
| 64 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 65 | job := route.Param(r.Context(), "job") |
| 66 | if jobBase64Encoded { |
| 67 | var err error |
| 68 | if job, err = decodeBase64(job); err != nil { |
| 69 | http.Error(w, fmt.Sprintf("invalid base64 encoding in job name %q: %v", job, err), http.StatusBadRequest) |
| 70 | logger.Debug("invalid base64 encoding in job name", "job", job, "err", err.Error()) |
| 71 | return |
| 72 | } |
| 73 | } |
| 74 | labelsString := route.Param(r.Context(), "labels") |
| 75 | labels, err := splitLabels(labelsString) |
| 76 | if err != nil { |
| 77 | http.Error(w, err.Error(), http.StatusBadRequest) |
| 78 | logger.Debug("failed to parse URL", "url", labelsString, "err", err.Error()) |
| 79 | return |
| 80 | } |
| 81 | if job == "" { |
| 82 | http.Error(w, "job name is required", http.StatusBadRequest) |
| 83 | logger.Debug("job name is required") |
| 84 | return |
| 85 | } |
| 86 | labels["job"] = job |
| 87 | |
| 88 | var metricFamilies map[string]*dto.MetricFamily |
| 89 | ctMediatype, ctParams, ctErr := mime.ParseMediaType(r.Header.Get("Content-Type")) |
| 90 | if ctErr == nil && ctMediatype == "application/vnd.google.protobuf" && |
| 91 | ctParams["encoding"] == "delimited" && |
| 92 | ctParams["proto"] == "io.prometheus.client.MetricFamily" { |
| 93 | metricFamilies = map[string]*dto.MetricFamily{} |
| 94 | unmarshaler := protodelim.UnmarshalOptions{ |
| 95 | MaxSize: -1, |
| 96 | } |
| 97 | in := bufio.NewReader(r.Body) |
| 98 | for { |
| 99 | mf := &dto.MetricFamily{} |
| 100 | if err = unmarshaler.UnmarshalFrom(in, mf); err != nil { |
| 101 | if err == io.EOF { |
| 102 | err = nil |
| 103 | } |
| 104 | break |
| 105 | } |
| 106 | metricFamilies[mf.GetName()] = mf |
| 107 | } |
| 108 | } else { |
| 109 | // We could do further content-type checks here, but the |
| 110 | // fallback for now will anyway be the text format |
| 111 | // version 0.0.4, so just go for it and see if it works. |
| 112 | parser := expfmt.NewTextParser(ValidationScheme) |
| 113 | metricFamilies, err = parser.TextToMetricFamilies(r.Body) |
| 114 | } |
| 115 | if err != nil { |
| 116 | http.Error(w, err.Error(), http.StatusBadRequest) |
searching dependent graphs…