faas/gateway/metrics/add_metrics.go
Alex Ellis (OpenFaaS Ltd) f8576d5b5d Return error from upstream when calling list functions
This is currently being hidden and needs to be bubbled up
into the logs to show an issue with calling list functions
during a deletion in faasd.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2021-02-27 20:18:34 +00:00

112 lines
2.9 KiB
Go

package metrics
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
types "github.com/openfaas/faas-provider/types"
)
// AddMetricsHandler wraps a http.HandlerFunc with Prometheus metrics
func AddMetricsHandler(handler http.HandlerFunc, prometheusQuery PrometheusQueryFetcher) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, r)
upstreamCall := recorder.Result()
if upstreamCall.Body == nil {
log.Println("Upstream call had empty body.")
return
}
defer upstreamCall.Body.Close()
upstreamBody, _ := ioutil.ReadAll(upstreamCall.Body)
if recorder.Code != http.StatusOK {
log.Printf("List functions responded with code %d, body: %s",
recorder.Code,
string(upstreamBody))
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("List functions responded with code %d", recorder.Code)))
return
}
var functions []types.FunctionStatus
err := json.Unmarshal(upstreamBody, &functions)
if err != nil {
log.Printf("Metrics upstream error: %s", err)
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Error parsing metrics from upstream provider/backend."))
return
}
expr := url.QueryEscape(`sum(gateway_function_invocation_total{function_name=~".*", code=~".*"}) by (function_name, code)`)
// expr := "sum(gateway_function_invocation_total%7Bfunction_name%3D~%22.*%22%2C+code%3D~%22.*%22%7D)+by+(function_name%2C+code)"
results, fetchErr := prometheusQuery.Fetch(expr)
if fetchErr != nil {
log.Printf("Error querying Prometheus API: %s\n", fetchErr.Error())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(upstreamBody)
return
}
mixIn(&functions, results)
bytesOut, marshalErr := json.Marshal(functions)
if marshalErr != nil {
log.Println(marshalErr)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(bytesOut)
}
}
func mixIn(functions *[]types.FunctionStatus, metrics *VectorQueryResponse) {
if functions == nil {
return
}
// Ensure values are empty first.
for i := range *functions {
(*functions)[i].InvocationCount = 0
}
for i, function := range *functions {
for _, v := range metrics.Data.Result {
if v.Metric.FunctionName == fmt.Sprintf("%s.%s", function.Name, function.Namespace) {
metricValue := v.Value[1]
switch metricValue.(type) {
case string:
f, strconvErr := strconv.ParseFloat(metricValue.(string), 64)
if strconvErr != nil {
log.Printf("Unable to convert value for metric: %s\n", strconvErr)
continue
}
(*functions)[i].InvocationCount += f
break
}
}
}
}
}