Label request metrics with standard HTTP methods only (closes #274) #277
@@ -685,7 +685,9 @@ See `configs/config.example.yml` for all options with defaults.
|
||||
`metrics.username` and `metrics.password` are set. A request's `handler` label
|
||||
is the pattern of the route it matches, such as `/v1/image/*`, never its path.
|
||||
Requests that match no route share the label `unmatched`: those the router
|
||||
answers 404 or 405, and CORS preflights
|
||||
answers 404 or 405, and CORS preflights. A request's `method` label is its
|
||||
method when that is `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`,
|
||||
`OPTIONS`, `CONNECT` or `TRACE`, and `other` for any other method
|
||||
- **Logging**: stdlib slog
|
||||
|
||||
## Entrypoints
|
||||
|
||||
@@ -30,6 +30,15 @@ P2: security: per-origin rate limiting
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-15 the request metrics record any method that is not a standard one as
|
||||
`other` (closes #274): their `method` label was each request's method as sent,
|
||||
and Go's HTTP server accepts any method name, so a client sending made-up
|
||||
methods added series to the Prometheus registry that were never removed. The
|
||||
label is now the request's method only for `GET`, `HEAD`, `POST`, `PUT`,
|
||||
`PATCH`, `DELETE`, `OPTIONS`, `CONNECT` and `TRACE`. The metrics library reads
|
||||
the method from the request it is handed, so the metrics middleware hands it a
|
||||
copy of the request carrying the label; the handlers still see the method the
|
||||
client sent.
|
||||
- 2026-10-15 logging out ends every login session (closes #256): a login session
|
||||
was a cookie with nothing kept on the server, so `/logout` removed it from one
|
||||
browser and a copy of it kept working for its 30 days. Every login is made
|
||||
|
||||
@@ -220,7 +220,10 @@ func (s *Middleware) CORS() func(http.Handler) http.Handler {
|
||||
// pattern of the chi route a request matches, such as /v1/image/*, or
|
||||
// "unmatched" for a request that matches none, so the routes fix how many
|
||||
// values the label has. It is never the path, with which any client could
|
||||
// add series to the registry without limit.
|
||||
// add series to the registry without limit. Its method label is the
|
||||
// request's method when that is a standard one, such as GET, and "other"
|
||||
// for any other: Go's HTTP server accepts any method name, so a label per
|
||||
// method would let a client add series the same way.
|
||||
func (s *Middleware) Metrics() func(http.Handler) http.Handler {
|
||||
mdlw := ghmm.New(ghmm.Config{
|
||||
Recorder: metrics.NewRecorder(metrics.Config{
|
||||
@@ -230,7 +233,19 @@ func (s *Middleware) Metrics() func(http.Handler) http.Handler {
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
std.Handler(routePattern(r), mdlw, next).ServeHTTP(w, r)
|
||||
// The metrics library takes the method label from the request
|
||||
// it is handed, so it is handed a shallow copy of r whose
|
||||
// method is the label. next is served r itself, through the
|
||||
// response writer the library wraps to see the status and size.
|
||||
labelled := r.WithContext(r.Context())
|
||||
labelled.Method = methodLabel(r.Method)
|
||||
|
||||
serveNext := http.HandlerFunc(
|
||||
func(wrapped http.ResponseWriter, _ *http.Request) {
|
||||
next.ServeHTTP(wrapped, r)
|
||||
})
|
||||
|
||||
std.Handler(routePattern(r), mdlw, serveNext).ServeHTTP(w, labelled)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -275,6 +290,20 @@ func routePattern(r *http.Request) string {
|
||||
return pattern
|
||||
}
|
||||
|
||||
// methodLabel returns method when it is one of the standard methods net/http
|
||||
// names: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, CONNECT and TRACE.
|
||||
// For any other method it returns "other".
|
||||
func methodLabel(method string) string {
|
||||
switch method {
|
||||
case http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut,
|
||||
http.MethodPatch, http.MethodDelete, http.MethodOptions,
|
||||
http.MethodConnect, http.MethodTrace:
|
||||
return method
|
||||
default:
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
|
||||
// MetricsAuth returns a basic auth middleware for the metrics endpoint.
|
||||
func (s *Middleware) MetricsAuth() func(http.Handler) http.Handler {
|
||||
return basicauth.New(
|
||||
|
||||
Reference in New Issue
Block a user