123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package ierrors
- import (
- "fmt"
- "net/http"
- "runtime"
- "strings"
- )
- type Option func(*Error)
- type Error struct {
- err error
- prefix string
- statusCode int
- publicMessage string
- shouldReport bool
- stack []uintptr
- }
- func (e *Error) Error() string {
- if len(e.prefix) > 0 {
- return fmt.Sprintf("%s: %s", e.prefix, e.err.Error())
- }
- 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 (e *Error) Callers() []uintptr {
- return e.stack
- }
- func (e *Error) FormatStackLines() []string {
- lines := make([]string, len(e.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 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]
- }
|