mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 20:38:08 +08:00
feat(log): print traceback when panics occur (#2166)
Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
)
|
||||
|
||||
type Stack struct {
|
||||
Frames []Frame `json:"stack"`
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
Name string `json:"function"`
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
}
|
||||
|
||||
// RecoveryHandler is a HTTP middleware that recovers from a panic.
|
||||
// It logs the panic and its traceback in json format, writes http.StatusInternalServerError
|
||||
// and continues to the next handler.
|
||||
func RecoveryHandler(log log.Logger) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
response.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
stack := Stack{
|
||||
Frames: getStacktrace(),
|
||||
}
|
||||
|
||||
recoveredErr, ok := err.(error)
|
||||
if ok {
|
||||
buf, err := json.Marshal(stack)
|
||||
if err == nil {
|
||||
log.Error().Err(recoveredErr).RawJSON("traceback", buf).Msg("panic recovered") //nolint: check-logs
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Process request
|
||||
next.ServeHTTP(response, request)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getStacktrace() []Frame {
|
||||
stack := []Frame{}
|
||||
//nolint: varnamelen
|
||||
pc := make([]uintptr, 64)
|
||||
|
||||
n := runtime.Callers(0, pc)
|
||||
if n == 0 {
|
||||
return []Frame{}
|
||||
}
|
||||
|
||||
// first three frames are from this file, don't need them.
|
||||
pc = pc[3:]
|
||||
frames := runtime.CallersFrames(pc)
|
||||
|
||||
// loop to get frames.
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
|
||||
// store this frame
|
||||
stack = append(stack, Frame{
|
||||
Name: frame.Function,
|
||||
File: frame.File,
|
||||
Line: frame.Line,
|
||||
})
|
||||
|
||||
// check whether there are more frames to process after this one.
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return stack
|
||||
}
|
||||
Reference in New Issue
Block a user