Report all query API HTTP errors in JSON format.

This commit is contained in:
Julius Volz 2015-03-27 16:48:03 +01:00
parent df314ead84
commit c9b76def4c

View file

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