mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-09-26 03:29:00 +02:00
Revised errors for better error reporting
This commit is contained in:
@@ -2,83 +2,147 @@ package ierrors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Option func(*Error)
|
||||
|
||||
type Error struct {
|
||||
StatusCode int
|
||||
Message string
|
||||
PublicMessage string
|
||||
Unexpected bool
|
||||
err error
|
||||
|
||||
prefix string
|
||||
statusCode int
|
||||
publicMessage string
|
||||
shouldReport bool
|
||||
|
||||
stack []uintptr
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *Error) FormatStack() string {
|
||||
if e.stack == nil {
|
||||
return ""
|
||||
if len(e.prefix) > 0 {
|
||||
return fmt.Sprintf("%s: %s", e.prefix, e.err.Error())
|
||||
}
|
||||
|
||||
return formatStack(e.stack)
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *Error) Cause() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *Error) StatusCode() int {
|
||||
if e.statusCode <= 0 {
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
return e.statusCode
|
||||
}
|
||||
|
||||
func (e *Error) PublicMessage() string {
|
||||
if len(e.publicMessage) == 0 {
|
||||
return "Internal error"
|
||||
}
|
||||
|
||||
return e.publicMessage
|
||||
}
|
||||
|
||||
func (e *Error) ShouldReport() bool {
|
||||
return e.shouldReport
|
||||
}
|
||||
|
||||
func (e *Error) StackTrace() []uintptr {
|
||||
return e.stack
|
||||
}
|
||||
|
||||
func New(status int, msg string, pub string) *Error {
|
||||
return &Error{
|
||||
StatusCode: status,
|
||||
Message: msg,
|
||||
PublicMessage: pub,
|
||||
}
|
||||
func (e *Error) Callers() []uintptr {
|
||||
return e.stack
|
||||
}
|
||||
|
||||
func NewUnexpected(msg string, skip int) *Error {
|
||||
return &Error{
|
||||
StatusCode: 500,
|
||||
Message: msg,
|
||||
PublicMessage: "Internal error",
|
||||
Unexpected: true,
|
||||
func (e *Error) FormatStackLines() []string {
|
||||
lines := make([]string, len(e.stack))
|
||||
|
||||
stack: callers(skip + 3),
|
||||
}
|
||||
}
|
||||
|
||||
func Wrap(err error, skip int) *Error {
|
||||
if ierr, ok := err.(*Error); ok {
|
||||
return ierr
|
||||
}
|
||||
return NewUnexpected(err.Error(), skip+1)
|
||||
}
|
||||
|
||||
func WrapWithPrefix(err error, skip int, prefix string) *Error {
|
||||
if ierr, ok := err.(*Error); ok {
|
||||
newErr := *ierr
|
||||
newErr.Message = fmt.Sprintf("%s: %s", prefix, ierr.Message)
|
||||
return &newErr
|
||||
}
|
||||
return NewUnexpected(fmt.Sprintf("%s: %s", prefix, err), skip+1)
|
||||
}
|
||||
|
||||
func callers(skip int) []uintptr {
|
||||
stack := make([]uintptr, 10)
|
||||
n := runtime.Callers(skip, stack)
|
||||
return stack[:n]
|
||||
}
|
||||
|
||||
func formatStack(stack []uintptr) string {
|
||||
lines := make([]string, len(stack))
|
||||
for i, pc := range stack {
|
||||
for i, pc := range e.stack {
|
||||
f := runtime.FuncForPC(pc)
|
||||
file, line := f.FileLine(pc)
|
||||
lines[i] = fmt.Sprintf("%s:%d %s", file, line, f.Name())
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n")
|
||||
return lines
|
||||
}
|
||||
|
||||
func (e *Error) FormatStack() string {
|
||||
return strings.Join(e.FormatStackLines(), "\n")
|
||||
}
|
||||
|
||||
func Wrap(err error, stackSkip int, opts ...Option) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var e *Error
|
||||
|
||||
if ierr, ok := err.(*Error); ok {
|
||||
// if we have some options, we need to copy the error to not modify the original one
|
||||
if len(opts) > 0 {
|
||||
ecopy := *ierr
|
||||
e = &ecopy
|
||||
} else {
|
||||
return ierr
|
||||
}
|
||||
} else {
|
||||
e = &Error{
|
||||
err: err,
|
||||
shouldReport: true,
|
||||
}
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(e)
|
||||
}
|
||||
|
||||
if len(e.stack) == 0 {
|
||||
e.stack = callers(stackSkip + 1)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func WithStatusCode(code int) Option {
|
||||
return func(e *Error) {
|
||||
e.statusCode = code
|
||||
}
|
||||
}
|
||||
|
||||
func WithPublicMessage(msg string) Option {
|
||||
return func(e *Error) {
|
||||
e.publicMessage = msg
|
||||
}
|
||||
}
|
||||
|
||||
func WithPrefix(prefix string) Option {
|
||||
return func(e *Error) {
|
||||
if len(e.prefix) > 0 {
|
||||
e.prefix = fmt.Sprintf("%s: %s", prefix, e.prefix)
|
||||
} else {
|
||||
e.prefix = prefix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithShouldReport(report bool) Option {
|
||||
return func(e *Error) {
|
||||
e.shouldReport = report
|
||||
}
|
||||
}
|
||||
|
||||
func callers(skip int) []uintptr {
|
||||
stack := make([]uintptr, 10)
|
||||
n := runtime.Callers(skip+2, stack)
|
||||
return stack[:n]
|
||||
}
|
||||
|
Reference in New Issue
Block a user