Expose Prometheus metrics on a separate port
Metrics are served on METRICS_PORT (default 2112, "off" to disable), never on the public port. Besides the Go runtime and process metrics this adds: - http_requests_total and http_request_duration_seconds per route pattern, from an Echo middleware. - musicserver_songs_requested_total and musicserver_songs_played_total by pick mode (random, low, classic). The mode is now stored with each queued song (migration 000006, not released yet). - Library size, sync duration, last successful sync, broken songs from the last full sync, and the database connection pool. Refs #35, #36, #37 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c62b65d7cd
commit
f79f51ea4c
28 files changed
+864
-70
No files matched your search
@@ -12,7 +12,9 @@ import (
|
||||
"music-server/internal/db"
|
||||
"music-server/internal/db/repository"
|
||||
"music-server/internal/logging"
|
||||
"music-server/internal/metrics"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -34,6 +36,8 @@ type Server struct {
|
||||
stats *backend.StatisticsService
|
||||
search *backend.SearchCache
|
||||
httpServer *http.Server
|
||||
// metrics serves /metrics on its own port; nil when metrics are off.
|
||||
metrics *metrics.Server
|
||||
}
|
||||
|
||||
// New creates a Server with all services wired up.
|
||||
@@ -56,6 +60,9 @@ func New(cfg config.Config, q repository.Querier, database Database) (*Server, e
|
||||
s.search = backend.NewSearchCache(s.music.SoundtrackNames)
|
||||
s.sync = backend.NewSyncService(q, cfg.MusicPath, s.afterSync)
|
||||
|
||||
if cfg.MetricsPort > 0 {
|
||||
s.metrics = metrics.NewServer(cfg.MetricsPort)
|
||||
}
|
||||
s.httpServer = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", cfg.Port),
|
||||
Handler: s.RegisterRoutes(),
|
||||
@@ -111,11 +118,21 @@ func NewServerInstance() *Server {
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to create server", zap.Error(err))
|
||||
}
|
||||
prometheus.MustRegister(metrics.NewPoolCollector(database.Pool))
|
||||
if err := s.sync.UpdateLibraryMetrics(ctx); err != nil {
|
||||
logger.Error("Failed to count the library", zap.Error(err))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ListenAndServe starts the HTTP server. It returns nil after a graceful shutdown.
|
||||
// ListenAndServe starts the HTTP server and the metrics server. It returns nil after a
|
||||
// graceful shutdown. The server keeps running without metrics if their port cannot be opened.
|
||||
func (s *Server) ListenAndServe() error {
|
||||
if s.metrics != nil {
|
||||
if err := s.metrics.Start(); err != nil {
|
||||
logging.GetLogger().Error("Failed to start the metrics server", zap.Error(err))
|
||||
}
|
||||
}
|
||||
logging.GetLogger().Info("Server starting", zap.String("address", s.httpServer.Addr))
|
||||
if err := s.httpServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
@@ -126,6 +143,9 @@ func (s *Server) ListenAndServe() error {
|
||||
// Shutdown stops accepting requests, waits for running requests and then closes the database.
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
err := s.httpServer.Shutdown(ctx)
|
||||
if s.metrics != nil {
|
||||
err = errors.Join(err, s.metrics.Shutdown(ctx))
|
||||
}
|
||||
s.sync.Wait()
|
||||
s.characters.Close()
|
||||
s.db.Close()
|
||||
|
||||
Reference in new issue
Block a user