func handleRenderPB(w http.ResponseWriter, req *http.Request, format string, responses []serverResponse) { metrics := make(map[string][]pb.FetchResponse) for _, r := range responses { var d pb.MultiFetchResponse err := proto.Unmarshal(r.response, &d) if err != nil { logger.Logf("error decoding protobuf response from server:%s: req:%s: err=%s", r.server, req.URL.RequestURI(), err) logger.Traceln("\n" + hex.Dump(r.response)) Metrics.RenderErrors.Add(1) continue } for _, m := range d.Metrics { metrics[m.GetName()] = append(metrics[m.GetName()], *m) } } var multi pb.MultiFetchResponse if len(metrics) == 0 { err := fmt.Sprintf("no decoded responses to merge for req: %s", req.URL.RequestURI()) logger.Logln(err) http.Error(w, err, http.StatusInternalServerError) Metrics.RenderErrors.Add(1) return } for name, decoded := range metrics { logger.Tracef("request: %s: %q %+v", req.URL.RequestURI(), name, decoded) if len(decoded) == 1 { logger.Debugf("only one decoded responses to merge for req: %q %s", name, req.URL.RequestURI()) m := decoded[0] multi.Metrics = append(multi.Metrics, &m) continue } // Use the metric with the highest resolution as our base var highest int for i, d := range decoded { if d.GetStepTime() < decoded[highest].GetStepTime() { highest = i } } decoded[0], decoded[highest] = decoded[highest], decoded[0] metric := decoded[0] mergeValues(req, &metric, decoded) multi.Metrics = append(multi.Metrics, &metric) } returnRender(w, format, multi) }
func MarshalProtobuf(results []*MetricData) ([]byte, error) { response := pb.MultiFetchResponse{} for _, metric := range results { response.Metrics = append(response.Metrics, &((*metric).FetchResponse)) } b, err := response.Marshal() if err != nil { return nil, err } return b, nil }
func marshalProtobuf(results []*metricData) []byte { response := pb.MultiFetchResponse{} for _, metric := range results { response.Metrics = append(response.Metrics, &((*metric).FetchResponse)) } b, err := response.Marshal() if err != nil { logger.Logf("proto.Marshal: %v", err) } return b }
func fetchHandler(wr http.ResponseWriter, req *http.Request) { // URL: /render/?target=the.metric.name&format=pickle&from=1396008021&until=1396022421 Metrics.RenderRequests.Add(1) req.ParseForm() metric := req.FormValue("target") format := req.FormValue("format") from := req.FormValue("from") until := req.FormValue("until") t0 := time.Now() // Make sure we log which metric caused a panic() defer func() { if r := recover(); r != nil { var buf [1024]byte runtime.Stack(buf[:], false) logger.Logf("panic handling request: %s\n%s\n", req.RequestURI, string(buf[:])) } }() if format != "json" && format != "pickle" && format != "protobuf" { Metrics.RenderErrors.Add(1) logger.Logf("dropping invalid uri (format=%s): %s", format, req.URL.RequestURI()) http.Error(wr, "Bad request (unsupported format)", http.StatusBadRequest) return } files, leafs := expandGlobs(metric) var badTime bool i, err := strconv.Atoi(from) if err != nil { logger.Debugf("fromTime (%s) invalid: %s (in %s)", from, err, req.URL.RequestURI()) badTime = true } fromTime := int(i) i, err = strconv.Atoi(until) if err != nil { logger.Debugf("untilTime (%s) invalid: %s (in %s)", from, err, req.URL.RequestURI()) badTime = true } untilTime := int(i) if badTime { Metrics.RenderErrors.Add(1) http.Error(wr, "Bad request (invalid from/until time)", http.StatusBadRequest) return } var multi pb.MultiFetchResponse for i, metric := range files { if !leafs[i] { log.Printf("skipping directory = %q\n", metric) // can't fetch a directory continue } path := config.WhisperData + "/" + strings.Replace(metric, ".", "/", -1) + ".wsp" w, err := whisper.Open(path) if err != nil { // the FE/carbonzipper often requests metrics we don't have // We shouldn't really see this any more -- expandGlobs() should filter them out Metrics.NotFound.Add(1) log.Printf("error opening %q: %v\n", path, err) continue } points, err := w.Fetch(fromTime, untilTime) w.Close() if err != nil { Metrics.RenderErrors.Add(1) logger.Logf("failed to fetch points from %s: %s", path, err) continue } if points == nil { Metrics.NotFound.Add(1) logger.Debugf("Metric time range not found: metric=%s from=%d to=%d ", metric, fromTime, untilTime) continue } values := points.Values() fromTime := int32(points.FromTime()) untilTime := int32(points.UntilTime()) step := int32(points.Step()) response := pb.FetchResponse{ Name: proto.String(metric), StartTime: &fromTime, StopTime: &untilTime, StepTime: &step, Values: make([]float64, len(values)), IsAbsent: make([]bool, len(values)), } for i, p := range values { if math.IsNaN(p) { response.Values[i] = 0 response.IsAbsent[i] = true } else { response.Values[i] = p response.IsAbsent[i] = false } } multi.Metrics = append(multi.Metrics, &response) } var b []byte switch format { case "json": wr.Header().Set("Content-Type", "application/json") b, err = json.Marshal(multi) case "protobuf": wr.Header().Set("Content-Type", "application/protobuf") b, err = proto.Marshal(&multi) case "pickle": // transform protobuf data into what pickle expects //[{'start': 1396271100, 'step': 60, 'name': 'metric', //'values': [9.0, 19.0, None], 'end': 1396273140} var response []map[string]interface{} for _, metric := range multi.GetMetrics() { var m map[string]interface{} m = make(map[string]interface{}) m["start"] = metric.StartTime m["step"] = metric.StepTime m["end"] = metric.StopTime m["name"] = metric.Name mv := make([]interface{}, len(metric.Values)) for i, p := range metric.Values { if metric.IsAbsent[i] { mv[i] = nil } else { mv[i] = p } } m["values"] = mv response = append(response, m) } wr.Header().Set("Content-Type", "application/pickle") var buf bytes.Buffer pEnc := pickle.NewEncoder(&buf) err = pEnc.Encode(response) b = buf.Bytes() } if err != nil { Metrics.RenderErrors.Add(1) logger.Logf("failed to create %s data for %s: %s", format, "<metric>", err) return } wr.Write(b) logger.Debugf("fetch: served %q from %d to %d in %v", metric, fromTime, untilTime, time.Since(t0)) }
func renderHandler(w http.ResponseWriter, req *http.Request) { Metrics.FetchRequests.Add(1) target := req.FormValue("target") format := req.FormValue("format") from := req.FormValue("from") until := req.FormValue("until") frint, _ := strconv.Atoi(from) unint, _ := strconv.Atoi(until) if format != "json" && format != "protobuf" { http.Error(w, "bad request", http.StatusBadRequest) return } matches := findMetrics(target) var multi pb.MultiFetchResponse for _, m := range matches { target := m.GetPath() var metric string if prefix, _, ok := parseTopK(target); ok { metric = prefix } else { metric = target } metrics := Whispers.Fetch(metric) if metrics == nil { continue } points := metrics.Fetch(metric, int32(frint), int32(unint)) if points == nil { continue } fromTime := int32(points.From) untilTime := int32(points.Until) step := int32(points.Step) response := pb.FetchResponse{ Name: &target, StartTime: &fromTime, StopTime: &untilTime, StepTime: &step, Values: make([]float64, len(points.Values)), IsAbsent: make([]bool, len(points.Values)), } for i, p := range points.Values { if math.IsNaN(p) { response.Values[i] = 0 response.IsAbsent[i] = true } else { response.Values[i] = p response.IsAbsent[i] = false } } multi.Metrics = append(multi.Metrics, &response) } var b []byte switch format { case "json": w.Header().Set("Content-Type", "application/json") b, _ = json.Marshal(multi) case "protobuf": w.Header().Set("Content-Type", "application/protobuf") b, _ = proto.Marshal(&multi) } w.Write(b) }