Extend http status metrics by method and path

This commit is contained in:
Ingo Oppermann
2024-10-10 12:18:22 +02:00
parent 05e4118e0c
commit 91874e6caf
4 changed files with 39 additions and 26 deletions

View File

@@ -16,7 +16,7 @@ type Config struct {
// Skipper defines a function to skip middleware.
Skipper middleware.Skipper
Logger log.Logger
Status func(code int)
Status func(code int, method, path string, size int64, ttfb time.Duration)
}
var DefaultConfig = Config{
@@ -76,10 +76,15 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
res.Writer = writer
req.Body = reader
if w.ttfb.IsZero() {
w.ttfb = start
}
latency := time.Since(start)
ttfb := time.Since(w.ttfb)
if config.Status != nil {
config.Status(res.Status)
config.Status(res.Status, req.Method, c.Path(), w.size, ttfb)
}
if raw != "" {
@@ -87,16 +92,17 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
}
logger := config.Logger.WithFields(log.Fields{
"client": c.RealIP(),
"method": req.Method,
"path": path,
"proto": req.Proto,
"status": res.Status,
"status_text": http.StatusText(res.Status),
"tx_size_bytes": w.size,
"rx_size_bytes": r.size,
"latency_ms": latency.Milliseconds(),
"user_agent": req.Header.Get("User-Agent"),
"client": c.RealIP(),
"method": req.Method,
"path": path,
"proto": req.Proto,
"status": res.Status,
"status_text": http.StatusText(res.Status),
"tx_size_bytes": w.size,
"rx_size_bytes": r.size,
"latency_ms": latency.Milliseconds(),
"latency_ttfb_ms": ttfb.Milliseconds(),
"user_agent": req.Header.Get("User-Agent"),
})
logger.Debug().Log("")
@@ -110,12 +116,16 @@ type sizeWriter struct {
http.ResponseWriter
size int64
ttfb time.Time
}
func (w *sizeWriter) Write(body []byte) (int, error) {
n, err := w.ResponseWriter.Write(body)
w.size += int64(n)
if w.ttfb.IsZero() {
w.ttfb = time.Now()
}
return n, err
}

View File

@@ -30,9 +30,11 @@ package http
import (
"fmt"
"maps"
"net/http"
"strings"
"sync"
"time"
"github.com/datarhei/core/v16/cluster"
cfgstore "github.com/datarhei/core/v16/config/store"
@@ -166,7 +168,7 @@ type server struct {
metrics struct {
lock sync.Mutex
status map[int]uint64
status map[string]uint64
}
}
@@ -186,7 +188,7 @@ func NewServer(config Config) (serverhandler.Server, error) {
readOnly: config.ReadOnly,
}
s.metrics.status = map[int]uint64{}
s.metrics.status = map[string]uint64{}
s.filesystems = map[string]*filesystem{}
@@ -344,11 +346,13 @@ func NewServer(config Config) (serverhandler.Server, error) {
s.middleware.log = mwlog.NewWithConfig(mwlog.Config{
Logger: s.logger,
Status: func(code int) {
Status: func(code int, method, path string, size int64, ttfb time.Duration) {
key := fmt.Sprintf("%d:%s:%s", code, method, path)
s.metrics.lock.Lock()
defer s.metrics.lock.Unlock()
s.metrics.status[code]++
s.metrics.status[key]++
},
})
@@ -483,15 +487,13 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.router.ServeHTTP(w, r)
}
func (s *server) HTTPStatus() map[int]uint64 {
status := map[int]uint64{}
func (s *server) HTTPStatus() map[string]uint64 {
status := map[string]uint64{}
s.metrics.lock.Lock()
defer s.metrics.lock.Unlock()
for code, value := range s.metrics.status {
status[code] = value
}
maps.Copy(status, s.metrics.status)
return status
}

View File

@@ -4,5 +4,5 @@ import "net/http"
type Server interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
HTTPStatus() map[int]uint64
HTTPStatus() map[string]uint64
}

View File

@@ -1,7 +1,7 @@
package monitor
import (
"strconv"
"strings"
"github.com/datarhei/core/v16/http/server"
"github.com/datarhei/core/v16/monitor/metric"
@@ -19,7 +19,7 @@ func NewHTTPCollector(name string, handler server.Server) metric.Collector {
name: name,
}
c.statusDescr = metric.NewDesc("http_status", "Total return status", []string{"name", "code"})
c.statusDescr = metric.NewDesc("http_status", "Total return status count", []string{"name", "code", "method", "path"})
return c
}
@@ -39,8 +39,9 @@ func (c *httpCollector) Collect() metric.Metrics {
metrics := metric.NewMetrics()
for code, count := range status {
metrics.Add(metric.NewValue(c.statusDescr, float64(count), c.name, strconv.Itoa(code)))
for key, count := range status {
vals := strings.SplitN(key, ":", 3)
metrics.Add(metric.NewValue(c.statusDescr, float64(count), c.name, vals[0], vals[1], vals[2]))
}
return metrics