mirror of
https://github.com/datarhei/core.git
synced 2025-12-24 13:07:56 +08:00
Extend http status metrics by method and path
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user