Background
Depends on: "Expose Prometheus /metrics on a separate port".
The Grafana MusicServer dashboard has panels for Requests/s, Requests/s by route, p95 latency by route and Error rate (5xx %). These expect:
http_requests_total{method, route, code} (counter)
http_request_duration_seconds_bucket{method, route, ...} (histogram)
What to do
Add an Echo v5 middleware to internal/metrics. Use c.Path(), the registered route pattern (for example /api/v1/music/info), not the raw URI. This keeps the label count small; query strings and IDs must never end up in labels. Status resolution uses echo.ResolveResponseStatus, the same helper Echo's own RequestLogger uses.
var (
httpRequests = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "HTTP requests by method, route and status code.",
}, []string{"method", "route", "code"})
// Middleware records request count and latency per route.
func Middleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
start := time.Now()
err := next(c)
route := c.Path()
if route == "" {
route = "unmatched" // 404s etc. - don't use the raw URL
}
_, status := echo.ResolveResponseStatus(c.Response(), err)
method := c.Request().Method
httpRequests.WithLabelValues(method, route, strconv.Itoa(status)).Inc()
httpDuration.WithLabelValues(method, route).Observe(time.Since(start).Seconds())
return err
}
}
}
Register it in internal/server/routes.go right after logging.RequestLogger():
e.Use(logging.RequestLogger())
e.Use(metrics.Middleware())
Note: /assets/* and the / static handler will show up as their wildcard pattern, which is fine. If the static files get too noisy, skip them with if strings.HasPrefix(route, "/assets") { return err }.
Acceptance criteria
not done
http_requests_total and http_request_duration_seconds_bucket appear on /metrics
not done
route label values are route patterns, never full URLs with query strings
not done
Unit test: a request to a test route increments the counter with the expected labels (prometheus/testutil.ToFloat64)
not done
Grafana dashboard panels Requests/s, p95 latency, Error rate show data
Background
Depends on: "Expose Prometheus /metrics on a separate port".
The Grafana MusicServer dashboard has panels for Requests/s, Requests/s by route, p95 latency by route and Error rate (5xx %). These expect:
http_requests_total{method, route, code} (counter)
http_request_duration_seconds_bucket{method, route, ...} (histogram)
What to do
Add an Echo v5 middleware to internal/metrics. Use c.Path(), the registered route pattern (for example /api/v1/music/info), not the raw URI. This keeps the label count small; query strings and IDs must never end up in labels. Status resolution uses echo.ResolveResponseStatus, the same helper Echo's own RequestLogger uses.
package metrics
import (
"strconv"
"time"
"github.com/labstack/echo/v5"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
httpRequests = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "HTTP requests by method, route and status code.",
}, []string{"method", "route", "code"})
httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request latency by method and route.",
Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 30},
}, []string{"method", "route"})
)
// Middleware records request count and latency per route.
func Middleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
start := time.Now()
err := next(c)
route := c.Path()
if route == "" {
route = "unmatched" // 404s etc. - don't use the raw URL
}
_, status := echo.ResolveResponseStatus(c.Response(), err)
method := c.Request().Method
httpRequests.WithLabelValues(method, route, strconv.Itoa(status)).Inc()
httpDuration.WithLabelValues(method, route).Observe(time.Since(start).Seconds())
return err
}
}
}
Register it in internal/server/routes.go right after logging.RequestLogger():
e.Use(logging.RequestLogger())
e.Use(metrics.Middleware())
Note: /assets/* and the / static handler will show up as their wildcard pattern, which is fine. If the static files get too noisy, skip them with if strings.HasPrefix(route, "/assets") { return err }.
Acceptance criteria
not done
http_requests_total and http_request_duration_seconds_bucket appear on /metrics
not done
route label values are route patterns, never full URLs with query strings
not done
Unit test: a request to a test route increments the counter with the expected labels (prometheus/testutil.ToFloat64)
not done
Grafana dashboard panels Requests/s, p95 latency, Error rate show data
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Background
Depends on: "Expose Prometheus /metrics on a separate port".
The Grafana MusicServer dashboard has panels for Requests/s, Requests/s by route, p95 latency by route and Error rate (5xx %). These expect:
http_requests_total{method, route, code} (counter)
http_request_duration_seconds_bucket{method, route, ...} (histogram)
What to do
Add an Echo v5 middleware to internal/metrics. Use c.Path(), the registered route pattern (for example /api/v1/music/info), not the raw URI. This keeps the label count small; query strings and IDs must never end up in labels. Status resolution uses echo.ResolveResponseStatus, the same helper Echo's own RequestLogger uses.
package metrics
import (
"strconv"
"time"
)
var (
httpRequests = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "HTTP requests by method, route and status code.",
}, []string{"method", "route", "code"})
)
// Middleware records request count and latency per route.
func Middleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
start := time.Now()
err := next(c)
}
Register it in internal/server/routes.go right after logging.RequestLogger():
e.Use(logging.RequestLogger())
e.Use(metrics.Middleware())
Note: /assets/* and the / static handler will show up as their wildcard pattern, which is fine. If the static files get too noisy, skip them with if strings.HasPrefix(route, "/assets") { return err }.
Acceptance criteria
not done
http_requests_total and http_request_duration_seconds_bucket appear on /metrics
not done
route label values are route patterns, never full URLs with query strings
not done
Unit test: a request to a test route increments the counter with the expected labels (prometheus/testutil.ToFloat64)
not done
Grafana dashboard panels Requests/s, p95 latency, Error rate show data