From c9b76def4ca5f5e7450b22e6c00a750b2e3c920c Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Fri, 27 Mar 2015 16:48:03 +0100 Subject: [PATCH] Report all query API HTTP errors in JSON format. --- web/api/query.go | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/web/api/query.go b/web/api/query.go index 4e764088e..ae539dee8 100644 --- a/web/api/query.go +++ b/web/api/query.go @@ -40,6 +40,12 @@ func setAccessControlHeaders(w http.ResponseWriter) { w.Header().Set("Access-Control-Expose-Headers", "Date") } +func httpJSONError(w http.ResponseWriter, err error, code int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + fmt.Fprintln(w, ast.ErrorToJSON(err)) +} + func parseTimestampOrNow(t string) (clientmodel.Timestamp, error) { if t == "" { return clientmodel.Now(), nil @@ -63,27 +69,17 @@ func parseDuration(d string) (time.Duration, error) { // Query handles the /api/query endpoint. func (serv MetricsService) Query(w http.ResponseWriter, r *http.Request) { setAccessControlHeaders(w) + w.Header().Set("Content-Type", "application/json") params := httputils.GetQueryParams(r) expr := params.Get("expr") - asText := params.Get("asText") timestamp, err := parseTimestampOrNow(params.Get("timestamp")) if err != nil { - http.Error(w, fmt.Sprintf("invalid query timestamp %s", err), http.StatusBadRequest) + httpJSONError(w, fmt.Errorf("invalid query timestamp %s", err), http.StatusBadRequest) return } - var format ast.OutputFormat - // BUG(julius): Use Content-Type negotiation. - if asText == "" { - format = ast.JSON - w.Header().Set("Content-Type", "application/json") - } else { - format = ast.Text - w.Header().Set("Content-Type", "text/plain") - } - exprNode, err := rules.LoadExprFromString(expr) if err != nil { fmt.Fprint(w, ast.ErrorToJSON(err)) @@ -91,7 +87,7 @@ func (serv MetricsService) Query(w http.ResponseWriter, r *http.Request) { } queryStats := stats.NewTimerGroup() - result := ast.EvalToString(exprNode, timestamp, format, serv.Storage, queryStats) + result := ast.EvalToString(exprNode, timestamp, ast.JSON, serv.Storage, queryStats) glog.V(1).Infof("Instant query: %s\nQuery stats:\n%s\n", expr, queryStats) fmt.Fprint(w, result) } @@ -106,19 +102,19 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) { duration, err := parseDuration(params.Get("range")) if err != nil { - http.Error(w, fmt.Sprintf("invalid query range: %s", err), http.StatusBadRequest) + httpJSONError(w, fmt.Errorf("invalid query range: %s", err), http.StatusBadRequest) return } step, err := parseDuration(params.Get("step")) if err != nil { - http.Error(w, fmt.Sprintf("invalid query resolution: %s", err), http.StatusBadRequest) + httpJSONError(w, fmt.Errorf("invalid query resolution: %s", err), http.StatusBadRequest) return } end, err := parseTimestampOrNow(params.Get("end")) if err != nil { - http.Error(w, fmt.Sprintf("invalid query timestamp: %s", err), http.StatusBadRequest) + httpJSONError(w, fmt.Errorf("invalid query timestamp: %s", err), http.StatusBadRequest) return } // TODO(julius): Remove this special-case handling a while after PromDash and @@ -178,15 +174,15 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) { // Metrics handles the /api/metrics endpoint. func (serv MetricsService) Metrics(w http.ResponseWriter, r *http.Request) { setAccessControlHeaders(w) + w.Header().Set("Content-Type", "application/json") metricNames := serv.Storage.GetLabelValuesForLabelName(clientmodel.MetricNameLabel) sort.Sort(metricNames) resultBytes, err := json.Marshal(metricNames) if err != nil { glog.Error("Error marshalling metric names: ", err) - http.Error(w, err.Error(), http.StatusInternalServerError) + httpJSONError(w, fmt.Errorf("Error marshalling metric names: %s", err), http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "application/json") w.Write(resultBytes) }