TheMusicServerdashboard in Grafana is ready and waiting for data.
What to do
Expose Prometheus metrics on aseparate port(METRICS_PORT, default2112). Keeping it separate means/metricsis never reachable through the publicmusic.sanplex.xyzdomain (Pangolin only forwards the main port).
The default registry already includes theGo runtime and process collectors, so with no other changes you getgo_goroutines,go_memstats_*, GC stats,process_cpu_seconds_total,process_resident_memory_bytesandprocess_start_time_seconds. These feed the Memory, Goroutines, CPU and Uptime panels on the dashboard.
Acceptance criteria
not donecurl localhost:2112/metricsinside the container returnsgo_goroutines
not done/metricson the main port (8080) isnotserved
not doneThe metrics server shuts down gracefully with the main server
not doneMETRICS_PORT=offdisables it (handy for tests)
Follow-ups: request metrics middleware, app-specific metrics, and the Unraid deploy changes (separate issues).
Background
A Grafana + Prometheus + Loki stack now runs on Unraid (Grafana: http://192.168.86.100:3001/, Prometheus: http://192.168.86.100:9090/). Prometheus already has a musicserver scrape job that expects metrics at:
Instance Prometheus target
prod 192.168.86.100:9101
ref 192.168.86.100:9102
test 192.168.86.100:9103
The MusicServer dashboard in Grafana is ready and waiting for data.
What to do
Expose Prometheus metrics on a separate port (METRICS_PORT, default 2112). Keeping it separate means /metrics is never reachable through the public music.sanplex.xyz domain (Pangolin only forwards the main port).
go get github.com/prometheus/client_golang@latest
New package internal/metrics:
package metrics
// Start serves /metrics on METRICS_PORT (default 2112). Disabled when METRICS_PORT=off.
func Start() *http.Server {
port := os.Getenv("METRICS_PORT")
if port == "off" {
return nil
}
if port == "" {
port = "2112"
}
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
srv := &http.Server{Addr: ":" + port, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
go func() {
logging.GetLogger().Info("Metrics server starting", zap.String("address", srv.Addr))
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logging.GetLogger().Error("Metrics server error", zap.Error(err))
}
}()
return srv
}
func Shutdown(ctx context.Context, srv http.Server) {
if srv != nil {
_ = srv.Shutdown(ctx)
}
}
In cmd/main.go, call metricsServer := metrics.Start() before httpServer.ListenAndServe() and metrics.Shutdown(ctx, metricsServer) in gracefulShutdown.
EXPOSE 2112 in the Dockerfile (documentation only).
The default registry already includes the Go runtime and process collectors, so with no other changes you get go_goroutines, go_memstats_, GC stats, process_cpu_seconds_total, process_resident_memory_bytes and process_start_time_seconds. These feed the Memory, Goroutines, CPU and Uptime panels on the dashboard.
Acceptance criteria
not done
curl localhost:2112/metrics inside the container returns go_goroutines
not done
/metrics on the main port (8080) is not served
not done
The metrics server shuts down gracefully with the main server
not done
METRICS_PORT=off disables it (handy for tests)
Follow-ups: request metrics middleware, app-specific metrics, and the Unraid deploy changes (separate issues).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Background
A Grafana + Prometheus + Loki stack now runs on Unraid (Grafana: http://192.168.86.100:3001, Prometheus: http://192.168.86.100:9090). Prometheus already has a
musicserverscrape job that expects metrics at:The MusicServer dashboard in Grafana is ready and waiting for data.
What to do
Expose Prometheus metrics on a separate port (
METRICS_PORT, default2112). Keeping it separate means/metricsis never reachable through the publicmusic.sanplex.xyzdomain (Pangolin only forwards the main port).go get github.com/prometheus/client_golang@latestinternal/metrics:"context"
"errors"
"net/http"
"os"
"time"
)
// Start serves /metrics on METRICS_PORT (default 2112). Disabled when METRICS_PORT=off.
func Start() *http.Server {
port := os.Getenv("METRICS_PORT")
if port == "off" {
return nil
}
if port == "" {
port = "2112"
}
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
srv := &http.Server{Addr: ":" + port, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
go func() {
logging.GetLogger().Info("Metrics server starting", zap.String("address", srv.Addr))
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logging.GetLogger().Error("Metrics server error", zap.Error(err))
}
}()
return srv
}
func Shutdown(ctx context.Context, srv *http.Server) {
if srv != nil {
_ = srv.Shutdown(ctx)
}
}" class="epitaxy-diff epitaxy-fence rounded-lg overflow-clip" style="box-sizing: border-box; border: 0px solid color(srgb 1 1 1 / 0.1); margin: 0px; padding: 0px; outline-color: rgb(85, 152, 231); scrollbar-width: thin; app-region: initial; border-radius: 8px; overflow: clip; --diffs-font-size: calc(.75rem * 1); --diffs-line-height: calc(1.0625rem * 1); --diffs-font-family: "anthropic-mono", ui-monospace, monospace, "SF Mono", ui-monospace, Menlo, Consolas, monospace; --diffs-header-font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "PingFang SC", "PingFang TC", "Hiragino Sans", "Apple SD Gothic Neo", sans-serif; --diffs-addition-color-override: #32d74b; --diffs-deletion-color-override: #ff2c56; --diffs-modified-color-override: #6da7ec; --diffs-gap-inline: calc(.5rem * 1); --diffs-gap-block: calc(.25rem * 1); position: relative;">
cmd/main.go, callmetricsServer := metrics.Start()beforehttpServer.ListenAndServe()andmetrics.Shutdown(ctx, metricsServer)ingracefulShutdown.EXPOSE 2112in theDockerfile(documentation only).The default registry already includes the Go runtime and process collectors, so with no other changes you get
go_goroutines,go_memstats_*, GC stats,process_cpu_seconds_total,process_resident_memory_bytesandprocess_start_time_seconds. These feed the Memory, Goroutines, CPU and Uptime panels on the dashboard.Acceptance criteria
curl localhost:2112/metricsinside the container returnsgo_goroutines/metricson the main port (8080) is not servedMETRICS_PORT=offdisables it (handy for tests)Follow-ups: request metrics middleware, app-specific metrics, and the Unraid deploy changes (separate issues).
Background A Grafana + Prometheus + Loki stack now runs on Unraid (Grafana: http://192.168.86.100:3001/, Prometheus: http://192.168.86.100:9090/). Prometheus already has a musicserver scrape job that expects metrics at:Instance Prometheus target
prod 192.168.86.100:9101
ref 192.168.86.100:9102
test 192.168.86.100:9103
The MusicServer dashboard in Grafana is ready and waiting for data.
What to do
Expose Prometheus metrics on a separate port (METRICS_PORT, default 2112). Keeping it separate means /metrics is never reachable through the public music.sanplex.xyz domain (Pangolin only forwards the main port).
go get github.com/prometheus/client_golang@latest
New package internal/metrics:
package metrics
import (
"context"
"errors"
"net/http"
"os"
"time"
)
// Start serves /metrics on METRICS_PORT (default 2112). Disabled when METRICS_PORT=off.
func Start() *http.Server {
port := os.Getenv("METRICS_PORT")
if port == "off" {
return nil
}
if port == "" {
port = "2112"
}
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
srv := &http.Server{Addr: ":" + port, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
go func() {
logging.GetLogger().Info("Metrics server starting", zap.String("address", srv.Addr))
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logging.GetLogger().Error("Metrics server error", zap.Error(err))
}
}()
return srv
}
func Shutdown(ctx context.Context, srv http.Server) {
if srv != nil {
_ = srv.Shutdown(ctx)
}
}
In cmd/main.go, call metricsServer := metrics.Start() before httpServer.ListenAndServe() and metrics.Shutdown(ctx, metricsServer) in gracefulShutdown.
EXPOSE 2112 in the Dockerfile (documentation only).
The default registry already includes the Go runtime and process collectors, so with no other changes you get go_goroutines, go_memstats_, GC stats, process_cpu_seconds_total, process_resident_memory_bytes and process_start_time_seconds. These feed the Memory, Goroutines, CPU and Uptime panels on the dashboard.
Acceptance criteria
not done
curl localhost:2112/metrics inside the container returns go_goroutines
not done
/metrics on the main port (8080) is not served
not done
The metrics server shuts down gracefully with the main server
not done
METRICS_PORT=off disables it (handy for tests)
Follow-ups: request metrics middleware, app-specific metrics, and the Unraid deploy changes (separate issues).