diff --git a/metric/metric.go b/metric/metric.go index cc7980495..d868f5524 100644 --- a/metric/metric.go +++ b/metric/metric.go @@ -103,10 +103,11 @@ func (m *metric) Run(ctx context.Context) (err error) { return } if m.config.Addr != "" { - var errCh = make(chan error) - http.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{})) + var errCh = make(chan error, 1) + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{})) go func() { - errCh <- http.ListenAndServe(m.config.Addr, nil) + errCh <- http.ListenAndServe(m.config.Addr, mux) }() select { case err = <-errCh: diff --git a/metric/metric_run_test.go b/metric/metric_run_test.go new file mode 100644 index 000000000..5fe24e7f3 --- /dev/null +++ b/metric/metric_run_test.go @@ -0,0 +1,37 @@ +package metric + +import ( + "context" + "testing" + + "github.com/anyproto/any-sync/app" + "github.com/prometheus/client_golang/prometheus" +) + +func TestRun_DoesNotPanicWithMultipleInstances(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("metric.Run should not panic with multiple metric instances: %v", r) + } + }() + + first := newTestMetric() + if err := first.Run(context.Background()); err == nil { + t.Fatal("first metric run should return listen error with invalid test address") + } + + second := newTestMetric() + if err := second.Run(context.Background()); err == nil { + t.Fatal("second metric run should return listen error with invalid test address") + } +} + +func newTestMetric() *metric { + return &metric{ + registry: prometheus.NewRegistry(), + // Intentionally invalid address to avoid creating long-lived listeners in unit tests. + config: Config{Addr: "127.0.0.1"}, + a: &app.App{}, + syncMetrics: map[string]SyncMetric{}, + } +}