Files
Jason Staack b840047e19 feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:30:44 -05:00

60 lines
1.7 KiB
Go

package observability
import (
"context"
"log/slog"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// StartServer starts an HTTP server for Prometheus metrics and health checks.
//
// The server exposes:
// - GET /metrics — Prometheus metrics endpoint
// - GET /health — Liveness probe (returns 200 with {"status":"ok"})
//
// The server shuts down gracefully when ctx is cancelled. It runs in a
// goroutine and does not block the caller.
func StartServer(ctx context.Context, addr string) *http.Server {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/health", healthHandler)
srv := &http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
// Start serving in a goroutine.
go func() {
slog.Info("observability server starting", "addr", addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
slog.Error("observability server error", "error", err)
}
}()
// Graceful shutdown when context is cancelled.
go func() {
<-ctx.Done()
slog.Info("observability server shutting down")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
slog.Error("observability server shutdown error", "error", err)
}
slog.Info("observability server stopped")
}()
return srv
}
// healthHandler returns a simple liveness response.
func healthHandler(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"status":"ok"}`))
}