73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
package httpd
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"runtime/debug"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
|
|
"github.com/aoagents/agent-orchestrator/backend/internal/httpd/envelope"
|
|
"github.com/aoagents/agent-orchestrator/backend/internal/ports"
|
|
)
|
|
|
|
func recoverTelemetry(log *slog.Logger, sink ports.EventSink) func(http.Handler) http.Handler {
|
|
log = loggerOrDefault(log)
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
defer func() {
|
|
if rec := recover(); rec != nil {
|
|
stack := string(debug.Stack())
|
|
log.Error("http handler panic",
|
|
"id", middleware.GetReqID(r.Context()),
|
|
"method", r.Method,
|
|
"path", r.URL.Path,
|
|
"panic", fmt.Sprint(rec),
|
|
"stack", stack,
|
|
)
|
|
if sink != nil {
|
|
sink.Emit(r.Context(), ports.TelemetryEvent{
|
|
Name: "ao.daemon.panic",
|
|
Source: "http",
|
|
OccurredAt: time.Now().UTC(),
|
|
Level: ports.TelemetryLevelError,
|
|
RequestID: middleware.GetReqID(r.Context()),
|
|
Payload: map[string]any{
|
|
"method": r.Method,
|
|
"path": r.URL.Path,
|
|
"panic_kind": telemetryPanicKind(rec),
|
|
},
|
|
})
|
|
}
|
|
writeRecoveredError(w, r)
|
|
}
|
|
}()
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|
|
|
|
func telemetryPanicKind(rec any) string {
|
|
switch rec.(type) {
|
|
case error:
|
|
return "error"
|
|
case string:
|
|
return "string"
|
|
default:
|
|
return "other"
|
|
}
|
|
}
|
|
|
|
func writeRecoveredError(w http.ResponseWriter, r *http.Request) {
|
|
if strings.HasPrefix(r.URL.Path, "/api/") {
|
|
envelope.WriteAPIError(w, r, http.StatusInternalServerError, "internal_error", "INTERNAL_ERROR", "Internal server error", nil)
|
|
return
|
|
}
|
|
envelope.WriteJSON(w, http.StatusInternalServerError, map[string]any{
|
|
"status": "error",
|
|
})
|
|
}
|