Add stability hardening and self-heal recovery
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -311,11 +312,11 @@ func NewHandler(opts HandlerOptions) http.Handler {
|
||||
mux.HandleFunc("GET /", h.handlePage)
|
||||
|
||||
h.mux = mux
|
||||
return mux
|
||||
return recoverMiddleware(mux)
|
||||
}
|
||||
|
||||
func (h *handler) startMetricsCollector() {
|
||||
go func() {
|
||||
goRecoverLoop("metrics collector", 2*time.Second, func() {
|
||||
ticker := time.NewTicker(metricsCollectInterval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
@@ -326,7 +327,7 @@ func (h *handler) startMetricsCollector() {
|
||||
h.feedRings(sample)
|
||||
h.setLatestMetric(sample)
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func (h *handler) setLatestMetric(sample platform.LiveMetricSample) {
|
||||
@@ -347,7 +348,49 @@ func (h *handler) latestMetric() (platform.LiveMetricSample, bool) {
|
||||
|
||||
// ListenAndServe starts the HTTP server.
|
||||
func ListenAndServe(addr string, opts HandlerOptions) error {
|
||||
return http.ListenAndServe(addr, NewHandler(opts))
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: NewHandler(opts),
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
IdleTimeout: 2 * time.Minute,
|
||||
}
|
||||
return srv.ListenAndServe()
|
||||
}
|
||||
|
||||
type trackingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (w *trackingResponseWriter) WriteHeader(statusCode int) {
|
||||
w.wroteHeader = true
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (w *trackingResponseWriter) Write(p []byte) (int, error) {
|
||||
w.wroteHeader = true
|
||||
return w.ResponseWriter.Write(p)
|
||||
}
|
||||
|
||||
func recoverMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tw := &trackingResponseWriter{ResponseWriter: w}
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
slog.Error("http handler panic",
|
||||
"method", r.Method,
|
||||
"path", r.URL.Path,
|
||||
"panic", fmt.Sprint(rec),
|
||||
"stack", string(debug.Stack()),
|
||||
)
|
||||
if !tw.wroteHeader {
|
||||
http.Error(tw, "internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(tw, r)
|
||||
})
|
||||
}
|
||||
|
||||
// ── Infrastructure handlers ──────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user