feat(prometheus): add metrics to Subsonic API and Plugins (#4266)

* Add prometheus metrics to subsonic and plugins

* address feedback, do not log error if operation is not supported

* add missing timestamp and client to stats

* remove .view from subsonic route

* directly inject DataStore in Prometheus, to avoid having to pass it in every call

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Kendall Garner
2025-06-28 02:13:57 +00:00
committed by GitHub
parent 709714cfc0
commit 0cd15c1ddc
22 changed files with 246 additions and 89 deletions
+10 -1
View File
@@ -13,6 +13,7 @@ import (
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/core/artwork"
"github.com/navidrome/navidrome/core/external"
"github.com/navidrome/navidrome/core/metrics"
"github.com/navidrome/navidrome/core/playback"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
@@ -43,11 +44,13 @@ type Router struct {
scrobbler scrobbler.PlayTracker
share core.Share
playback playback.PlaybackServer
metrics metrics.Metrics
}
func New(ds model.DataStore, artwork artwork.Artwork, streamer core.MediaStreamer, archiver core.Archiver,
players core.Players, provider external.Provider, scanner scanner.Scanner, broker events.Broker,
playlists core.Playlists, scrobbler scrobbler.PlayTracker, share core.Share, playback playback.PlaybackServer,
metrics metrics.Metrics,
) *Router {
r := &Router{
ds: ds,
@@ -62,6 +65,7 @@ func New(ds model.DataStore, artwork artwork.Artwork, streamer core.MediaStreame
scrobbler: scrobbler,
share: share,
playback: playback,
metrics: metrics,
}
r.Handler = r.routes()
return r
@@ -69,6 +73,11 @@ func New(ds model.DataStore, artwork artwork.Artwork, streamer core.MediaStreame
func (api *Router) routes() http.Handler {
r := chi.NewRouter()
if conf.Server.Prometheus.Enabled {
r.Use(recordStats(api.metrics))
}
r.Use(postFormToQueryParams)
// Public
@@ -223,7 +232,7 @@ func h(r chi.Router, path string, f handler) {
})
}
// Add a Subsonic handler that requires a http.ResponseWriter (ex: stream, getCoverArt...)
// Add a Subsonic handler that requires an http.ResponseWriter (ex: stream, getCoverArt...)
func hr(r chi.Router, path string, f handlerRaw) {
handle := func(w http.ResponseWriter, r *http.Request) {
res, err := f(w, r)