fix(prometheus): report subsonic error code (#4282)

* fix(prometheus): report subsonic error code

* address feedback
This commit is contained in:
Kendall Garner
2025-06-30 15:54:02 +00:00
committed by GitHub
parent a559414ffa
commit f9c7cc5348
4 changed files with 46 additions and 5 deletions

View File

@@ -333,6 +333,7 @@ func sendResponse(w http.ResponseWriter, r *http.Request, payload *responses.Sub
sendError(w, r, err)
return
}
if payload.Status == responses.StatusOK {
if log.IsGreaterOrEqualTo(log.LevelTrace) {
log.Debug(r.Context(), "API: Successful response", "endpoint", r.URL.Path, "status", "OK", "body", string(response))
@@ -342,6 +343,17 @@ func sendResponse(w http.ResponseWriter, r *http.Request, payload *responses.Sub
} else {
log.Warn(r.Context(), "API: Failed response", "endpoint", r.URL.Path, "error", payload.Error.Code, "message", payload.Error.Message)
}
statusPointer, ok := r.Context().Value(subsonicErrorPointer).(*int32)
if ok && statusPointer != nil {
if payload.Status == responses.StatusOK {
*statusPointer = 0
} else {
*statusPointer = payload.Error.Code
}
}
if _, err := w.Write(response); err != nil {
log.Error(r, "Error sending response to client", "endpoint", r.URL.Path, "payload", string(response), err)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/navidrome/navidrome/utils/gg"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"golang.org/x/net/context"
)
var _ = Describe("sendResponse", func() {
@@ -109,4 +110,18 @@ var _ = Describe("sendResponse", func() {
})
})
It("updates status pointer when an error occurs", func() {
pointer := int32(0)
ctx := context.WithValue(r.Context(), subsonicErrorPointer, &pointer)
r = r.WithContext(ctx)
payload.Status = responses.StatusFailed
payload.Error = &responses.Error{Code: responses.ErrorDataNotFound}
sendResponse(w, r, payload)
Expect(w.Code).To(Equal(http.StatusOK))
Expect(pointer).To(Equal(responses.ErrorDataNotFound))
})
})

View File

@@ -226,21 +226,35 @@ func playerIDCookieName(userName string) string {
return cookieName
}
const subsonicErrorPointer = "subsonicErrorPointer"
func recordStats(metrics metrics.Metrics) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
status := int32(-1)
contextWithStatus := context.WithValue(r.Context(), subsonicErrorPointer, &status)
start := time.Now()
defer func() {
elapsed := time.Since(start).Milliseconds()
// We want to get the client name (even if not present for certain endpoints)
p := req.Params(r)
client, _ := p.String("c")
metrics.RecordRequest(r.Context(), strings.Replace(r.URL.Path, ".view", "", 1), r.Method, client, ww.Status(), time.Since(start).Milliseconds())
// If there is no Subsonic status (e.g., HTTP 501 not implemented), fallback to HTTP
if status == -1 {
status = int32(ww.Status())
}
shortPath := strings.Replace(r.URL.Path, ".view", "", 1)
metrics.RecordRequest(r.Context(), shortPath, r.Method, client, status, elapsed)
}()
next.ServeHTTP(ww, r)
next.ServeHTTP(ww, r.WithContext(contextWithStatus))
}
return http.HandlerFunc(fn)
}