Files
MusicServer/internal/metrics/server.go
SansanandClaude Opus 5.5 f79f51ea4c
CI / integration (push) Failing after 14s
CI / docker (push) Failing after 20s
CI / test (push) Successful in 1m10s
CI / publish (push) Skipped
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>
2026-10-11 21:57:58 +02:00

61 lines
1.6 KiB
Go

// Package metrics exposes Prometheus metrics: the Go runtime and process metrics that the
// default registry collects, HTTP request metrics and MusicServer's own metrics.
package metrics
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"time"
"music-server/internal/logging"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
)
// Server serves /metrics on a port of its own, so it is never reachable through the
// public port.
type Server struct {
httpServer *http.Server
}
// NewServer creates a metrics server for the given port.
func NewServer(port int) *Server {
mux := http.NewServeMux()
mux.Handle("GET /metrics", promhttp.Handler())
return &Server{httpServer: &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}}
}
// Handler returns the handler that serves /metrics.
func (s *Server) Handler() http.Handler {
return s.httpServer.Handler
}
// Start listens on the port and serves metrics in the background. It returns an error
// when the port cannot be opened.
func (s *Server) Start() error {
ln, err := net.Listen("tcp", s.httpServer.Addr)
if err != nil {
return err
}
logging.GetLogger().Info("Metrics server starting", zap.String("address", s.httpServer.Addr))
go func() {
if err := s.httpServer.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
logging.GetLogger().Error("Metrics server error", zap.Error(err))
}
}()
return nil
}
// Shutdown stops the metrics server.
func (s *Server) Shutdown(ctx context.Context) error {
return s.httpServer.Shutdown(ctx)
}