...
1 package emperror
2
3 import (
4 "fmt"
5 "io"
6
7 "github.com/pkg/errors"
8 )
9
10 type stackTracer interface {
11 StackTrace() errors.StackTrace
12 }
13
14
15 func StackTrace(err error) (errors.StackTrace, bool) {
16 st, ok := getStackTracer(err)
17 if ok {
18 return st.StackTrace(), true
19 }
20
21 return nil, false
22 }
23
24
25 func getStackTracer(err error) (stackTracer, bool) {
26 var st stackTracer
27
28 ForEachCause(err, func(err error) bool {
29 if s, ok := err.(stackTracer); ok {
30 st = s
31
32 return false
33 }
34
35 return true
36 })
37
38 return st, st != nil
39 }
40
41 type withStack struct {
42 err error
43 st stackTracer
44 }
45
46 func (w *withStack) Error() string {
47 return w.err.Error()
48 }
49
50 func (w *withStack) Cause() error {
51 return w.err
52 }
53
54 func (w *withStack) StackTrace() errors.StackTrace {
55 return w.st.StackTrace()
56 }
57
58
59 func (w *withStack) Format(s fmt.State, verb rune) {
60 switch verb {
61 case 'v':
62 if s.Flag('+') {
63 _, _ = fmt.Fprintf(s, "%+v", w.Cause())
64 return
65 }
66 fallthrough
67
68 case 's':
69 _, _ = io.WriteString(s, w.Error())
70
71 case 'q':
72 _, _ = fmt.Fprintf(s, "%q", w.Error())
73 }
74 }
75
76
77 func ExposeStackTrace(err error) error {
78 if err == nil {
79 return err
80 }
81
82 st, ok := getStackTracer(err)
83 if !ok {
84 return err
85 }
86
87 return &withStack{
88 err: err,
89 st: st,
90 }
91 }
92
View as plain text