From fd088454d1f14d09e29a17a677d8488c068b8f51 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Thu, 22 Nov 2018 23:08:54 +0000
Subject: [PATCH] Add instrumentation to the UNIX socket server

---
 unix/server.go | 61 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 54 insertions(+), 7 deletions(-)

diff --git a/unix/server.go b/unix/server.go
index 0bb6e2e..b5d92e1 100644
--- a/unix/server.go
+++ b/unix/server.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/coreos/go-systemd/activation"
+	"github.com/prometheus/client_golang/prometheus"
 	"github.com/theckman/go-flock"
 )
 
@@ -51,7 +52,7 @@ func NewUNIXSocketServer(socketPath string, h Handler) (*SocketServer, error) {
 	// successfully. We could remove it before starting, but that
 	// would be dangerous if another instance was listening on
 	// that socket. So we wrap socket access with a file lock.
-	lock := flock.NewFlock(socketPath + ".lock")
+	lock := flock.New(socketPath + ".lock")
 	locked, err := lock.TryLock()
 	if err != nil {
 		return nil, err
@@ -66,7 +67,7 @@ func NewUNIXSocketServer(socketPath string, h Handler) (*SocketServer, error) {
 	}
 
 	// Always try to unlink the socket before creating it.
-	os.Remove(socketPath)
+	os.Remove(socketPath) // nolint
 
 	l, err := net.ListenUnix("unix", addr)
 	if err != nil {
@@ -98,10 +99,10 @@ func NewSystemdSocketServer(h Handler) (*SocketServer, error) {
 // Waits for active connections to terminate before returning.
 func (s *SocketServer) Close() {
 	s.closing.Store(true)
-	s.l.Close()
+	s.l.Close() // nolint
 	s.wg.Wait()
 	if s.lock != nil {
-		s.lock.Unlock()
+		s.lock.Unlock() // nolint
 	}
 }
 
@@ -122,7 +123,7 @@ func (s *SocketServer) Serve() error {
 		s.wg.Add(1)
 		go func() {
 			s.handler.ServeConnection(conn)
-			conn.Close()
+			conn.Close() // nolint
 			s.wg.Done()
 		}()
 	}
@@ -198,11 +199,15 @@ func (w *lrWriter) WriteLineCRLF(data []byte) error {
 	return w.Writer.Flush()
 }
 
+// ServeConnection handles a new connection. It will accept multiple
+// requests on the same connection (or not, depending on the client
+// preference).
 func (l *LineServer) ServeConnection(nc net.Conn) {
+	totalConnections.Inc()
 	c := textproto.NewConn(nc)
 	rw := &lrWriter{bufio.NewWriter(nc)}
 	for {
-		nc.SetReadDeadline(time.Now().Add(l.IdleTimeout))
+		nc.SetReadDeadline(time.Now().Add(l.IdleTimeout)) // nolint
 		line, err := c.ReadLineBytes()
 		if err == io.EOF {
 			break
@@ -215,17 +220,59 @@ func (l *LineServer) ServeConnection(nc net.Conn) {
 		// handler with it.  Set a write deadline on the
 		// connection to allow the full RequestTimeout time to
 		// generate the response.
-		nc.SetWriteDeadline(time.Now().Add(l.RequestTimeout + l.WriteTimeout))
+		start := time.Now()
+		nc.SetWriteDeadline(start.Add(l.RequestTimeout + l.WriteTimeout)) // nolint
 		ctx, cancel := context.WithTimeout(context.Background(), l.RequestTimeout)
 		err = l.handler.ServeLine(ctx, rw, line)
+		elapsedMs := time.Since(start).Nanoseconds() / 1000000
+		requestLatencyHist.Observe(float64(elapsedMs))
 		cancel()
 
 		// Close the connection on error, or on empty response.
 		if err != nil {
+			totalRequests.With(prometheus.Labels{
+				"status": "error",
+			}).Inc()
 			if err != ErrCloseConnection {
 				log.Printf("request error: %v", err)
 			}
 			break
 		}
+		totalRequests.With(prometheus.Labels{
+			"status": "ok",
+		}).Inc()
 	}
 }
+
+// Instrumentation metrics.
+var (
+	totalConnections = prometheus.NewCounter(
+		prometheus.CounterOpts{
+			Name: "unix_connections_total",
+			Help: "Total number of connections.",
+		},
+	)
+	totalRequests = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Name: "unix_requests_total",
+			Help: "Total number of requests.",
+		},
+		[]string{"status"},
+	)
+	// Histogram buckets are tuned for the low-milliseconds range
+	// (the largest bucket sits at ~1s).
+	requestLatencyHist = prometheus.NewHistogram(
+		prometheus.HistogramOpts{
+			Name:    "unix_requests_latency_ms",
+			Help:    "Latency of requests.",
+			Buckets: prometheus.ExponentialBuckets(5, 1.4142, 16),
+		},
+	)
+)
+
+func init() {
+	prometheus.MustRegister(totalConnections)
+	prometheus.MustRegister(totalRequests)
+	prometheus.MustRegister(requestLatencyHist)
+
+}
-- 
GitLab