func createRenderResponse(metrics pb.MultiFetchResponse, missing interface{}) []map[string]interface{} { var response []map[string]interface{} for _, metric := range metrics.GetMetrics() { var pvalues []interface{} for i, v := range metric.Values { if metric.IsAbsent[i] { pvalues = append(pvalues, missing) } else { pvalues = append(pvalues, v) } } // create the response presponse := map[string]interface{}{ "start": metric.StartTime, "step": metric.StepTime, "end": metric.StopTime, "name": metric.Name, "values": pvalues, } response = append(response, presponse) } return response }
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)) }