From 6073cd79f63124a390b6f2d4879f4cc421538af7 Mon Sep 17 00:00:00 2001
From: Alex <alexellis2@gmail.com>
Date: Thu, 22 Dec 2016 21:27:07 +0000
Subject: [PATCH] Add metrics

---
 README.md                  |  2 +
 gateway/Dockerfile         | 12 ++++--
 gateway/metrics/metrics.go | 13 +++++++
 gateway/server.go          | 75 +++++++++++++++++++++++++++++---------
 4 files changed, 81 insertions(+), 21 deletions(-)
 create mode 100644 gateway/metrics/metrics.go

diff --git a/README.md b/README.md
index 02a72061..077a4e01 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
 # faas
 Functions as a service
 
+Minimum requirements: Docker 1.13
+
 gateway
 =======
 
diff --git a/gateway/Dockerfile b/gateway/Dockerfile
index 8476e7e3..ee309dfa 100644
--- a/gateway/Dockerfile
+++ b/gateway/Dockerfile
@@ -4,12 +4,16 @@ RUN go get -d github.com/docker/docker/api/types \
  && go get -d github.com/docker/docker/api/types/filters \
   && go get -d github.com/docker/docker/api/types/swarm \
    && go get -d github.com/docker/docker/client
+RUN go get github.com/gorilla/mux \
+    && go get github.com/prometheus/client_golang/prometheus
 
-WORKDIR /go/src/app/
-RUN go get github.com/gorilla/mux
+WORKDIR /go/src/github.com/alexellis/faas/gateway
+
+COPY metrics metrics
 COPY server.go .
+
+RUN find /go/src/github.com/alexellis/faas/gateway/
 RUN go build
 EXPOSE 8080
 
-CMD ["./app"]
-
+CMD ["./gateway"]
diff --git a/gateway/metrics/metrics.go b/gateway/metrics/metrics.go
new file mode 100644
index 00000000..bb3be41d
--- /dev/null
+++ b/gateway/metrics/metrics.go
@@ -0,0 +1,13 @@
+package metrics
+
+import (
+	"net/http"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+// Handler returns the global http.Handler that provides the prometheus
+// metrics format on GET requests
+func Handler() http.Handler {
+	return prometheus.Handler()
+}
diff --git a/gateway/server.go b/gateway/server.go
index e21c33bb..20bb29e1 100644
--- a/gateway/server.go
+++ b/gateway/server.go
@@ -5,17 +5,26 @@ import (
 	"context"
 	"log"
 	"net/http"
+	"time"
 
 	"io/ioutil"
 
 	"strconv"
 
+	"github.com/alexellis/faas/gateway/metrics"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/client"
 	"github.com/gorilla/mux"
+	"github.com/prometheus/client_golang/prometheus"
 )
 
+type MetricOptions struct {
+	GatewayRequestsTotal         prometheus.Counter
+	GatewayServerlessServedTotal prometheus.Counter
+	GatewayFunctions             prometheus.Histogram
+}
+
 func lookupSwarmService(serviceName string) (bool, error) {
 	var c *client.Client
 	var err error
@@ -30,34 +39,66 @@ func lookupSwarmService(serviceName string) (bool, error) {
 	return len(services) > 0, err
 }
 
-func proxy(w http.ResponseWriter, r *http.Request) {
-	if r.Method == "POST" {
-		log.Println(r.Header)
-		header := r.Header["X-Function"]
-		log.Println(header)
+func makeProxy(metrics MetricOptions) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		metrics.GatewayRequestsTotal.Inc()
 
-		exists, err := lookupSwarmService(header[0])
-		if err != nil {
-			log.Fatalln(err)
-		}
+		start := time.Now()
 
-		if exists == true {
-			// client := http.Client{Timeout: time.Second * 2}
-			requestBody, _ := ioutil.ReadAll(r.Body)
-			buf := bytes.NewBuffer(requestBody)
+		if r.Method == "POST" {
+			log.Println(r.Header)
+			header := r.Header["X-Function"]
+			log.Println(header)
 
-			response, err := http.Post("http://"+header[0]+":"+strconv.Itoa(8080)+"/", "text/plain", buf)
+			exists, err := lookupSwarmService(header[0])
 			if err != nil {
 				log.Fatalln(err)
 			}
-			responseBody, _ := ioutil.ReadAll(response.Body)
-			w.Write(responseBody)
+
+			if exists == true {
+				requestBody, _ := ioutil.ReadAll(r.Body)
+				buf := bytes.NewBuffer(requestBody)
+
+				response, err := http.Post("http://"+header[0]+":"+strconv.Itoa(8080)+"/", "text/plain", buf)
+				if err != nil {
+					log.Fatalln(err)
+				}
+				responseBody, _ := ioutil.ReadAll(response.Body)
+				w.Write(responseBody)
+				metrics.GatewayServerlessServedTotal.Inc()
+
+				metrics.GatewayFunctions.Observe(time.Since(start).Seconds())
+			}
 		}
 	}
 }
 
 func main() {
+	GatewayRequestsTotal := prometheus.NewCounter(prometheus.CounterOpts{
+		Name: "gateway_requests_total",
+		Help: "Total amount of HTTP requests to the gateway",
+	})
+	GatewayServerlessServedTotal := prometheus.NewCounter(prometheus.CounterOpts{
+		Name: "gateway_serverless_invocation_total",
+		Help: "Total amount of serverless function invocations",
+	})
+	GatewayFunctions := prometheus.NewHistogram(prometheus.HistogramOpts{
+		Name: "gateway_functions",
+		Help: "Gateway functions",
+	})
+
+	prometheus.Register(GatewayRequestsTotal)
+	prometheus.Register(GatewayServerlessServedTotal)
+	prometheus.Register(GatewayFunctions)
+
 	r := mux.NewRouter()
-	r.HandleFunc("/", proxy)
+	r.HandleFunc("/", makeProxy(MetricOptions{
+		GatewayRequestsTotal:         GatewayRequestsTotal,
+		GatewayServerlessServedTotal: GatewayServerlessServedTotal,
+		GatewayFunctions:             GatewayFunctions,
+	}))
+
+	metricsHandler := metrics.Handler()
+	r.Handle("/metrics", metricsHandler)
 	log.Fatal(http.ListenAndServe(":8080", r))
 }
-- 
GitLab