mirror of
https://github.com/openfaas/faas.git
synced 2025-06-18 20:16:37 +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
|
||||
taken = time.Duration(report.TimeTaken)
|
||||
trackTimeExact(taken, metrics, report.FunctionName)
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
@ -11,13 +11,10 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/openfaas/faas/gateway/metrics"
|
||||
"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).
|
||||
@ -31,11 +28,6 @@ const (
|
||||
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
|
||||
type BaseURLResolver interface {
|
||||
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
|
||||
type SingleHostBaseURLResolver struct {
|
||||
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
|
||||
func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueRequests queue.CanQueueRequests, pathTransformer URLPathTransformer) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
@ -37,6 +40,7 @@ func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueReque
|
||||
urlVal, urlErr := url.Parse(callbackURLHeader)
|
||||
if urlErr != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
w.Write([]byte(urlErr.Error()))
|
||||
return
|
||||
}
|
||||
@ -55,9 +59,7 @@ func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueReque
|
||||
CallbackURL: callbackURL,
|
||||
}
|
||||
|
||||
err = canQueueRequests.Queue(req)
|
||||
|
||||
if err != nil {
|
||||
if err = canQueueRequests.Queue(req); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
fmt.Println(err)
|
||||
|
@ -57,6 +57,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||||
WithLabelValues(service.Name).
|
||||
Set(float64(service.Replicas))
|
||||
}
|
||||
|
||||
e.metricOptions.ServiceReplicasGauge.Collect(ch)
|
||||
|
||||
e.metricOptions.ServiceMetrics.Counter.Collect(ch)
|
||||
|
@ -78,7 +78,7 @@ func BuildMetricsOptions() MetricOptions {
|
||||
Name: "requests_total",
|
||||
Help: "The total number of HTTP requests.",
|
||||
},
|
||||
[]string{"status"},
|
||||
[]string{"method", "path", "status"},
|
||||
)
|
||||
|
||||
serviceMetricOptions := &ServiceMetricOptions{
|
||||
|
Reference in New Issue
Block a user