@@ -5,13 +5,15 @@ import (
5
5
"bytes"
6
6
"errors"
7
7
"fmt"
8
+ "os"
8
9
"strings"
10
+ "unicode"
9
11
"unsafe"
10
12
)
11
13
12
14
var (
13
15
// DefaultPCSize defines max length of Erf program counters in PC() method.
14
- DefaultPCSize = int (4096 / unsafe .Sizeof (uintptr (0 )))
16
+ DefaultPCSize = int (uintptr ( os . Getpagesize ()) / unsafe .Sizeof (uintptr (0 )))
15
17
)
16
18
17
19
// Erf is an error type that wraps the underlying error that stores and formats the stack trace.
@@ -45,10 +47,12 @@ func (e *Erf) Unwrap() error {
45
47
//
46
48
// For '%x' and '%X':
47
49
// %x list all error messages by using indent and show StackTrace of errors by using format '%+s'.
50
+ // %+x similar with '%x', also shows tags.
48
51
// % x list all error messages by using indent and show StackTrace of errors by using format '% s'.
49
52
// %#x list all error messages by using indent and show StackTrace of errors by using format '%#s'.
50
53
// % #x list all error messages by using indent and show StackTrace of errors by using format '% #s'.
51
54
// %X show the first error message by using indent and show the StackTrace of error by using format '%+s'.
55
+ // %+X similar with '%X', also shows tags.
52
56
// % X show the first error message by using indent and show the StackTrace of error by using format '% s'.
53
57
// %#X show the first error message by using indent and show the StackTrace of error by using format '%#s'.
54
58
// % #X show the first error message by using indent and show the StackTrace of error by using format '% #s'.
@@ -79,15 +83,15 @@ func (e *Erf) Format(f fmt.State, verb rune) {
79
83
pad , wid , prec := getPadWidPrec (f )
80
84
format += fmt .Sprintf ("%d.%ds" , wid , prec )
81
85
padding , indent := bytes .Repeat ([]byte {pad }, wid ), bytes .Repeat ([]byte {pad }, prec )
82
- count := 0
86
+ newLine := false
83
87
for err := error (e ); err != nil ; {
88
+ if newLine {
89
+ buf .WriteRune ('\n' )
90
+ }
91
+ newLine = true
84
92
if e2 , ok := err .(* Erf ); ok {
85
- if count > 0 {
86
- buf .WriteRune ('\n' )
87
- }
88
- count ++
89
93
if ! f .Flag ('-' ) {
90
- for _ , line := range strings .Split (e2 .err . Error (), "\n " ) {
94
+ for _ , line := range strings .Split (e2 .Error (), "\n " ) {
91
95
buf .Write (padding )
92
96
buf .Write (indent )
93
97
buf .WriteString (line )
@@ -96,11 +100,44 @@ func (e *Erf) Format(f fmt.State, verb rune) {
96
100
}
97
101
buf .WriteString (fmt .Sprintf (format , e2 .StackTrace ()))
98
102
buf .WriteRune ('\n' )
103
+ if f .Flag ('+' ) {
104
+ tags := e2 .Tags ()
105
+ if len (tags ) > 0 {
106
+ buf .Write (padding )
107
+ buf .WriteString ("* " )
108
+ for idx , tag := range tags {
109
+ if idx > 0 {
110
+ buf .WriteRune (' ' )
111
+ }
112
+ v := "s"
113
+ for _ , r := range tag {
114
+ if unicode .IsSpace (r ) || r == '"' || r == '\'' {
115
+ v = "q"
116
+ break
117
+ }
118
+ }
119
+ buf .WriteString (fmt .Sprintf ("%" + v + "=%q" , tag , fmt .Sprintf ("%v" , e2 .Tag (tag ))))
120
+ }
121
+ buf .WriteRune ('\n' )
122
+ }
123
+ }
99
124
buf .Write (padding )
100
- if verb == 'X' {
101
- break
125
+ } else {
126
+ if ! f .Flag ('-' ) {
127
+ for _ , line := range strings .Split (err .Error (), "\n " ) {
128
+ buf .Write (padding )
129
+ buf .Write (indent )
130
+ buf .WriteString (line )
131
+ buf .WriteRune ('\n' )
132
+ }
133
+ buf .Write (padding )
134
+ } else {
135
+ newLine = false
102
136
}
103
137
}
138
+ if verb == 'X' {
139
+ break
140
+ }
104
141
if wErr , ok := err .(WrappedError ); ok {
105
142
err = wErr .Unwrap ()
106
143
} else {
@@ -125,7 +162,7 @@ func (e *Erf) Len() int {
125
162
126
163
// Arg returns an argument value on the given index. It panics if index is out of range.
127
164
func (e * Erf ) Arg (index int ) interface {} {
128
- if index < 0 || index >= e . Len ( ) {
165
+ if index < 0 || index >= len ( e . args ) {
129
166
panic ("index out of range" )
130
167
}
131
168
return e .args [index ]
@@ -142,6 +179,7 @@ func (e *Erf) Args() []interface{} {
142
179
}
143
180
144
181
// Attach attaches tags to arguments, if arguments are given.
182
+ // If tag is "", it passes attaching tag to corresponding argument.
145
183
// It panics for given errors:
146
184
// args are not using
147
185
// tags are already attached
@@ -171,18 +209,41 @@ func (e *Erf) Attach(tags ...string) *Erf {
171
209
return e
172
210
}
173
211
212
+ // Attach2 is similar with Attach except that it returns the error interface instead of the Erf pointer.
213
+ func (e * Erf ) Attach2 (tags ... string ) error {
214
+ return e .Attach (tags ... )
215
+ }
216
+
174
217
// Tag returns an argument value on the given tag. It returns nil if tag is not found.
175
218
func (e * Erf ) Tag (tag string ) interface {} {
176
219
index := - 1
177
220
if idx , ok := e .tagIndexes [tag ]; ok {
178
221
index = idx
179
222
}
180
- if index < 0 || index >= e . Len ( ) {
223
+ if index < 0 || index >= len ( e . args ) {
181
224
return nil
182
225
}
183
226
return e .args [index ]
184
227
}
185
228
229
+ // Tags returns all tags sequentially. It returns nil if tags are not attached.
230
+ func (e * Erf ) Tags () []string {
231
+ if e .tagIndexes == nil {
232
+ return nil
233
+ }
234
+ m := make (map [int ]string , len (e .tagIndexes ))
235
+ for tag , index := range e .tagIndexes {
236
+ m [index ] = tag
237
+ }
238
+ result := make ([]string , 0 , len (m ))
239
+ for i , j := 0 , len (m ); i < j ; i ++ {
240
+ if tag , ok := m [i ]; ok {
241
+ result = append (result , tag )
242
+ }
243
+ }
244
+ return result
245
+ }
246
+
186
247
// PC returns program counters.
187
248
func (e * Erf ) PC () []uintptr {
188
249
result := make ([]uintptr , len (e .pc ))
0 commit comments