mirror of
https://github.com/openfaas/faas.git
synced 2025-06-19 12:36:40 +00:00
Split out notifiers
- splits out notifiers and writes status for async handler Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
fca32a0e79
commit
f7cf7a6496
@ -27,5 +27,7 @@ func MakeAsyncReport(metrics metrics.MetricOptions) http.HandlerFunc {
|
|||||||
var taken time.Duration
|
var taken time.Duration
|
||||||
taken = time.Duration(report.TimeTaken)
|
taken = time.Duration(report.TimeTaken)
|
||||||
trackTimeExact(taken, metrics, report.FunctionName)
|
trackTimeExact(taken, metrics, report.FunctionName)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,10 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/openfaas/faas/gateway/metrics"
|
|
||||||
"github.com/openfaas/faas/gateway/types"
|
"github.com/openfaas/faas/gateway/types"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// functionMatcher parses out the service name (group 1) and rest of path (group 2).
|
// functionMatcher parses out the service name (group 1) and rest of path (group 2).
|
||||||
@ -31,11 +28,6 @@ const (
|
|||||||
pathIndex = 2 // pathIndex is the path i.e. /employee/:id/
|
pathIndex = 2 // pathIndex is the path i.e. /employee/:id/
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPNotifier notify about HTTP request/response
|
|
||||||
type HTTPNotifier interface {
|
|
||||||
Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BaseURLResolver URL resolver for upstream requests
|
// BaseURLResolver URL resolver for upstream requests
|
||||||
type BaseURLResolver interface {
|
type BaseURLResolver interface {
|
||||||
Resolve(r *http.Request) string
|
Resolve(r *http.Request) string
|
||||||
@ -140,78 +132,6 @@ func copyHeaders(destination http.Header, source *http.Header) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PrometheusServiceNotifier struct {
|
|
||||||
ServiceMetrics *metrics.ServiceMetricOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify about service metrics
|
|
||||||
func (psn PrometheusServiceNotifier) Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration) {
|
|
||||||
code := fmt.Sprintf("%d", statusCode)
|
|
||||||
psn.ServiceMetrics.Counter.WithLabelValues(code).Inc()
|
|
||||||
|
|
||||||
psn.ServiceMetrics.Histogram.WithLabelValues(method, urlToLabel(URL), code).Observe(duration.Seconds())
|
|
||||||
}
|
|
||||||
|
|
||||||
var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9]+`)
|
|
||||||
|
|
||||||
// converts a URL path to a string compatible with Prometheus label value.
|
|
||||||
func urlToLabel(path string) string {
|
|
||||||
result := invalidChars.ReplaceAllString(path, "_")
|
|
||||||
result = strings.ToLower(strings.Trim(result, "_"))
|
|
||||||
if result == "" {
|
|
||||||
result = "root"
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrometheusFunctionNotifier records metrics to Prometheus
|
|
||||||
type PrometheusFunctionNotifier struct {
|
|
||||||
Metrics *metrics.MetricOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify records metrics in Prometheus
|
|
||||||
func (p PrometheusFunctionNotifier) Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration) {
|
|
||||||
seconds := duration.Seconds()
|
|
||||||
serviceName := getServiceName(originalURL)
|
|
||||||
|
|
||||||
p.Metrics.GatewayFunctionsHistogram.
|
|
||||||
WithLabelValues(serviceName).
|
|
||||||
Observe(seconds)
|
|
||||||
|
|
||||||
code := strconv.Itoa(statusCode)
|
|
||||||
|
|
||||||
p.Metrics.GatewayFunctionInvocation.
|
|
||||||
With(prometheus.Labels{"function_name": serviceName, "code": code}).
|
|
||||||
Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServiceName(urlValue string) string {
|
|
||||||
var serviceName string
|
|
||||||
forward := "/function/"
|
|
||||||
if strings.HasPrefix(urlValue, forward) {
|
|
||||||
// With a path like `/function/xyz/rest/of/path?q=a`, the service
|
|
||||||
// name we wish to locate is just the `xyz` portion. With a postive
|
|
||||||
// match on the regex below, it will return a three-element slice.
|
|
||||||
// The item at index `0` is the same as `urlValue`, at `1`
|
|
||||||
// will be the service name we need, and at `2` the rest of the path.
|
|
||||||
matcher := functionMatcher.Copy()
|
|
||||||
matches := matcher.FindStringSubmatch(urlValue)
|
|
||||||
if len(matches) == hasPathCount {
|
|
||||||
serviceName = matches[nameIndex]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Trim(serviceName, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoggingNotifier notifies a log about a request
|
|
||||||
type LoggingNotifier struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify a log about a request
|
|
||||||
func (LoggingNotifier) Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration) {
|
|
||||||
log.Printf("Forwarded [%s] to %s - [%d] - %f seconds", method, originalURL, statusCode, duration.Seconds())
|
|
||||||
}
|
|
||||||
|
|
||||||
// SingleHostBaseURLResolver resolves URLs against a single BaseURL
|
// SingleHostBaseURLResolver resolves URLs against a single BaseURL
|
||||||
type SingleHostBaseURLResolver struct {
|
type SingleHostBaseURLResolver struct {
|
||||||
BaseURL string
|
BaseURL string
|
||||||
|
91
gateway/handlers/notifiers.go
Normal file
91
gateway/handlers/notifiers.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/openfaas/faas/gateway/metrics"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPNotifier notify about HTTP request/response
|
||||||
|
type HTTPNotifier interface {
|
||||||
|
Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrometheusServiceNotifier notifier for core service endpoints
|
||||||
|
type PrometheusServiceNotifier struct {
|
||||||
|
ServiceMetrics *metrics.ServiceMetricOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify about service metrics
|
||||||
|
func (psn PrometheusServiceNotifier) Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration) {
|
||||||
|
code := fmt.Sprintf("%d", statusCode)
|
||||||
|
path := urlToLabel(URL)
|
||||||
|
psn.ServiceMetrics.Counter.WithLabelValues(code, method, path).Inc()
|
||||||
|
psn.ServiceMetrics.Histogram.WithLabelValues(method, path, code).Observe(duration.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9]+`)
|
||||||
|
|
||||||
|
// converts a URL path to a string compatible with Prometheus label value.
|
||||||
|
func urlToLabel(path string) string {
|
||||||
|
result := invalidChars.ReplaceAllString(path, "_")
|
||||||
|
result = strings.ToLower(strings.Trim(result, "_"))
|
||||||
|
if result == "" {
|
||||||
|
result = "root"
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrometheusFunctionNotifier records metrics to Prometheus
|
||||||
|
type PrometheusFunctionNotifier struct {
|
||||||
|
Metrics *metrics.MetricOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify records metrics in Prometheus
|
||||||
|
func (p PrometheusFunctionNotifier) Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration) {
|
||||||
|
seconds := duration.Seconds()
|
||||||
|
serviceName := getServiceName(originalURL)
|
||||||
|
|
||||||
|
p.Metrics.GatewayFunctionsHistogram.
|
||||||
|
WithLabelValues(serviceName).
|
||||||
|
Observe(seconds)
|
||||||
|
|
||||||
|
code := strconv.Itoa(statusCode)
|
||||||
|
|
||||||
|
p.Metrics.GatewayFunctionInvocation.
|
||||||
|
With(prometheus.Labels{"function_name": serviceName, "code": code}).
|
||||||
|
Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceName(urlValue string) string {
|
||||||
|
var serviceName string
|
||||||
|
forward := "/function/"
|
||||||
|
if strings.HasPrefix(urlValue, forward) {
|
||||||
|
// With a path like `/function/xyz/rest/of/path?q=a`, the service
|
||||||
|
// name we wish to locate is just the `xyz` portion. With a postive
|
||||||
|
// match on the regex below, it will return a three-element slice.
|
||||||
|
// The item at index `0` is the same as `urlValue`, at `1`
|
||||||
|
// will be the service name we need, and at `2` the rest of the path.
|
||||||
|
matcher := functionMatcher.Copy()
|
||||||
|
matches := matcher.FindStringSubmatch(urlValue)
|
||||||
|
if len(matches) == hasPathCount {
|
||||||
|
serviceName = matches[nameIndex]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Trim(serviceName, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoggingNotifier notifies a log about a request
|
||||||
|
type LoggingNotifier struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify a log about a request
|
||||||
|
func (LoggingNotifier) Notify(method string, URL string, originalURL string, statusCode int, duration time.Duration) {
|
||||||
|
log.Printf("Forwarded [%s] to %s - [%d] - %f seconds", method, originalURL, statusCode, duration.Seconds())
|
||||||
|
}
|
@ -17,12 +17,15 @@ import (
|
|||||||
// MakeQueuedProxy accepts work onto a queue
|
// MakeQueuedProxy accepts work onto a queue
|
||||||
func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueRequests queue.CanQueueRequests, pathTransformer URLPathTransformer) http.HandlerFunc {
|
func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueRequests queue.CanQueueRequests, pathTransformer URLPathTransformer) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer r.Body.Close()
|
if r.Body != nil {
|
||||||
|
defer r.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -37,6 +40,7 @@ func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueReque
|
|||||||
urlVal, urlErr := url.Parse(callbackURLHeader)
|
urlVal, urlErr := url.Parse(callbackURLHeader)
|
||||||
if urlErr != nil {
|
if urlErr != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
|
||||||
w.Write([]byte(urlErr.Error()))
|
w.Write([]byte(urlErr.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,9 +59,7 @@ func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueReque
|
|||||||
CallbackURL: callbackURL,
|
CallbackURL: callbackURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = canQueueRequests.Queue(req)
|
if err = canQueueRequests.Queue(req); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -57,6 +57,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||||||
WithLabelValues(service.Name).
|
WithLabelValues(service.Name).
|
||||||
Set(float64(service.Replicas))
|
Set(float64(service.Replicas))
|
||||||
}
|
}
|
||||||
|
|
||||||
e.metricOptions.ServiceReplicasGauge.Collect(ch)
|
e.metricOptions.ServiceReplicasGauge.Collect(ch)
|
||||||
|
|
||||||
e.metricOptions.ServiceMetrics.Counter.Collect(ch)
|
e.metricOptions.ServiceMetrics.Counter.Collect(ch)
|
||||||
|
@ -78,7 +78,7 @@ func BuildMetricsOptions() MetricOptions {
|
|||||||
Name: "requests_total",
|
Name: "requests_total",
|
||||||
Help: "The total number of HTTP requests.",
|
Help: "The total number of HTTP requests.",
|
||||||
},
|
},
|
||||||
[]string{"status"},
|
[]string{"method", "path", "status"},
|
||||||
)
|
)
|
||||||
|
|
||||||
serviceMetricOptions := &ServiceMetricOptions{
|
serviceMetricOptions := &ServiceMetricOptions{
|
||||||
|
Reference in New Issue
Block a user