Skip to content
This repository was archived by the owner on Nov 21, 2023. It is now read-only.

Commit 3c61724

Browse files
Merge pull request #2 from goinsane/develop
v1.0.0
2 parents e875f28 + 543b6a1 commit 3c61724

File tree

7 files changed

+97
-69
lines changed

7 files changed

+97
-69
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
Package erf provides error management with stack trace.
66
Erf is an error type that wraps the underlying error that stores and formats the stack trace.
7-
Please see [godoc](https://pkg.go.dev/github.com/goinsane/erf) and [example](https://github.com/goinsane/erf/tree/master/example).
7+
Please see [godoc](https://pkg.go.dev/github.com/goinsane/erf) and [examples](https://github.com/goinsane/erf/tree/master/examples).

erf.go

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ import (
55
"bytes"
66
"errors"
77
"fmt"
8+
"strings"
89
"unsafe"
910
)
1011

12+
var (
13+
// DefaultPCSize defines max length of Erf program counters in PC() method.
14+
DefaultPCSize = int(4096 / unsafe.Sizeof(uintptr(0)))
15+
)
16+
1117
// Erf is an error type that wraps the underlying error that stores and formats the stack trace.
1218
type Erf struct {
1319
err error
@@ -35,46 +41,60 @@ func (e *Erf) Format(f fmt.State, verb rune) {
3541
buf := bytes.NewBuffer(nil)
3642
switch verb {
3743
case 's', 'v':
38-
buf.WriteString(e.err.Error())
39-
if f.Flag('+') {
40-
format := "%+"
41-
for _, r := range []rune{'-', '#'} {
42-
if f.Flag(int(r)) {
43-
format += string(r)
44-
}
45-
}
46-
wid, prec := 1, 1
47-
if f.Flag('-') {
48-
wid, prec = 2, 2
49-
}
50-
if w, ok := f.Width(); ok {
51-
wid = w
52-
}
53-
if p, ok := f.Precision(); ok {
54-
prec = p
44+
if !f.Flag('+') {
45+
buf.WriteString(e.err.Error())
46+
break
47+
}
48+
format := "%+"
49+
for _, r := range []rune{'-', '#'} {
50+
if f.Flag(int(r)) {
51+
format += string(r)
5552
}
56-
format += fmt.Sprintf("%d.%d", wid, prec)
57-
format += "s"
58-
buf.WriteRune('\n')
59-
buf.WriteString(fmt.Sprintf(format, e.StackTrace()))
53+
}
54+
pad, wid, prec := byte('\t'), 0, 1
55+
if f.Flag('-') {
56+
pad = ' '
57+
prec = 2
58+
}
59+
if w, ok := f.Width(); ok {
60+
wid = w
61+
}
62+
if p, ok := f.Precision(); ok {
63+
prec = p
64+
}
65+
format += fmt.Sprintf("%d.%d", wid, prec)
66+
format += "s"
67+
padding := bytes.Repeat([]byte{pad}, wid)
68+
indent := bytes.Repeat([]byte{pad}, prec)
69+
for _, line := range strings.Split(e.err.Error(), "\n") {
70+
buf.Write(padding)
71+
buf.Write(indent)
72+
buf.WriteString(line)
6073
buf.WriteRune('\n')
61-
if !f.Flag('0') {
62-
for err := e.Unwrap(); err != nil; {
63-
if e2, ok := err.(*Erf); ok {
64-
buf.WriteRune('\n')
65-
buf.WriteString(e2.Error())
66-
buf.WriteRune('\n')
67-
buf.WriteString(fmt.Sprintf(format, e2.StackTrace()))
74+
}
75+
buf.WriteString(fmt.Sprintf(format, e.StackTrace()))
76+
buf.WriteRune('\n')
77+
if !f.Flag('0') {
78+
for err := e.Unwrap(); err != nil; {
79+
if e2, ok := err.(*Erf); ok {
80+
buf.WriteRune('\n')
81+
for _, line := range strings.Split(e2.err.Error(), "\n") {
82+
buf.Write(padding)
83+
buf.Write(indent)
84+
buf.WriteString(line)
6885
buf.WriteRune('\n')
6986
}
70-
if wErr, ok := err.(WrappedError); ok {
71-
err = wErr.Unwrap()
72-
} else {
73-
err = nil
74-
}
87+
buf.WriteString(fmt.Sprintf(format, e2.StackTrace()))
88+
buf.WriteRune('\n')
89+
}
90+
if wErr, ok := err.(WrappedError); ok {
91+
err = wErr.Unwrap()
92+
} else {
93+
err = nil
7594
}
7695
}
7796
}
97+
buf.WriteRune('\n')
7898
}
7999
if buf.Len() > 0 {
80100
_, _ = f.Write(buf.Bytes())
@@ -94,7 +114,7 @@ func (e *Erf) Len() int {
94114
// Arg returns an argument value on the given index. It panics if index is out of range.
95115
func (e *Erf) Arg(index int) interface{} {
96116
if index < 0 || index >= e.Len() {
97-
panic("index is out of range")
117+
panic("index out of range")
98118
}
99119
return e.args[index]
100120
}
@@ -122,8 +142,11 @@ func (e *Erf) Attach(tags ...string) *Erf {
122142
}
123143
tagIndexes := make(map[string]int, len(tags))
124144
for index, tag := range tags {
145+
if tag == "" {
146+
continue
147+
}
125148
if _, ok := tagIndexes[tag]; ok {
126-
panic("tag is already defined")
149+
panic("tag already defined")
127150
}
128151
tagIndexes[tag] = index
129152
}
@@ -138,7 +161,7 @@ func (e *Erf) Tag(tag string) interface{} {
138161
index = idx
139162
}
140163
if index < 0 || index >= e.Len() {
141-
panic("tag is not found")
164+
panic("tag not found")
142165
}
143166
return e.args[index]
144167
}
@@ -156,7 +179,7 @@ func (e *Erf) StackTrace() *StackTrace {
156179
}
157180

158181
func (e *Erf) initialize(skip int) {
159-
e.pc = getPC(int(4096/unsafe.Sizeof(uintptr(0))), skip)
182+
e.pc = PC(DefaultPCSize, skip)
160183
}
161184

162185
// New creates a new Erf object with the given text.

examples/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# examples
2+
3+
- [example1](https://github.com/goinsane/erf/blob/master/examples/example1.go)
4+
> go run examples/example1.go
5+

example/main.go renamed to examples/example1.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,47 @@ func main() {
4444
if err := Foo(-1); err != nil {
4545
fmt.Printf("%v\n", err)
4646
}
47-
fmt.Println("### \n")
47+
fmt.Printf("### \n\n")
4848

4949
fmt.Println("### Foo: show with stack trace")
5050
if err := Foo(-2); err != nil {
5151
fmt.Printf("%+v\n", err)
5252
}
53-
fmt.Println("### \n")
53+
fmt.Printf("### \n\n")
5454

5555
fmt.Println("### Foo: show with stack trace, and use space as whitespace instead of tab")
5656
if err := Foo(-3); err != nil {
5757
fmt.Printf("%+-v\n", err)
5858
}
59-
fmt.Println("### \n")
59+
fmt.Printf("### \n\n")
6060

6161
fmt.Println("### Foo: show with last stack trace")
6262
if err := Foo(-4); err != nil {
6363
fmt.Printf("%+0v\n", err)
6464
}
65-
fmt.Println("### \n")
65+
fmt.Printf("### \n\n")
6666

6767
fmt.Println("### Foo: show with stack trace with only file names except full path")
6868
if err := Foo(-5); err != nil {
6969
fmt.Printf("%+#v\n", err)
7070
}
71-
fmt.Println("### \n")
71+
fmt.Printf("### \n\n")
7272

7373
fmt.Println("### Foo: show with stack trace with 2 whitespace chars of padding and 1 whitespace char of indent")
7474
if err := Foo(-6); err != nil {
7575
fmt.Printf("%+2.1v\n", err)
7676
}
77-
fmt.Println("### \n")
77+
fmt.Printf("### \n\n")
7878

7979
fmt.Println("### Bar: show with stack trace")
8080
if err := Bar(-7); err != nil {
8181
fmt.Printf("%+v\n", err)
8282
}
83-
fmt.Println("### \n")
83+
fmt.Printf("### \n\n")
8484

8585
fmt.Println("### Baz: show with stack trace")
8686
if err := Baz(-8); err != nil {
8787
fmt.Printf("%+v\n", err)
8888
}
89-
fmt.Println("### \n")
89+
fmt.Printf("### \n\n")
9090
}

interfaces.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package erf
22

3-
type Wrapped interface {
4-
Unwrap() error
5-
}
6-
73
type WrappedError interface {
8-
Wrapped
94
error
5+
Unwrap() error
106
}

stack.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"fmt"
66
"runtime"
7-
"strings"
87
)
98

109
// StackCaller stores the information of stack caller.
@@ -34,6 +33,7 @@ func (c StackCaller) Format(f fmt.State, verb rune) {
3433
prec = p
3534
}
3635
padding := bytes.Repeat([]byte{pad}, wid)
36+
indent := bytes.Repeat([]byte{pad}, prec)
3737
var str string
3838
buf.Write(padding)
3939
str = "???"
@@ -44,11 +44,12 @@ func (c StackCaller) Format(f fmt.State, verb rune) {
4444
if f.Flag('+') {
4545
buf.WriteRune('\n')
4646
buf.Write(padding)
47+
buf.Write(indent)
4748
str = trimSrcPath(c.File)
4849
if f.Flag('#') {
4950
str = trimDirs(str)
5051
}
51-
buf.WriteString(fmt.Sprintf("%s%s:%d +%#x", strings.Repeat(string(pad), prec), str, c.Line, c.PC-c.Entry))
52+
buf.WriteString(fmt.Sprintf("%s:%d +%#x", str, c.Line, c.PC-c.Entry))
5253
}
5354
}
5455
if buf.Len() > 0 {
@@ -69,15 +70,17 @@ func NewStackTrace(pc ...uintptr) *StackTrace {
6970
callers: make([]StackCaller, 0, len(pc)),
7071
}
7172
copy(t.pc, pc)
72-
frames := runtime.CallersFrames(t.pc)
73-
for {
74-
frame, more := frames.Next()
75-
caller := StackCaller{
76-
Frame: frame,
77-
}
78-
t.callers = append(t.callers, caller)
79-
if !more {
80-
break
73+
if len(t.pc) > 0 {
74+
frames := runtime.CallersFrames(t.pc)
75+
for {
76+
frame, more := frames.Next()
77+
caller := StackCaller{
78+
Frame: frame,
79+
}
80+
t.callers = append(t.callers, caller)
81+
if !more {
82+
break
83+
}
8184
}
8285
}
8386
return t
@@ -147,7 +150,7 @@ func (t *StackTrace) Len() int {
147150
// Caller returns a StackCaller on the given index. It panics if index is out of range.
148151
func (t *StackTrace) Caller(index int) StackCaller {
149152
if index < 0 || index >= t.Len() {
150-
panic("index is out of range")
153+
panic("index out of range")
151154
}
152155
return t.callers[index]
153156
}

util.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import (
77
"strings"
88
)
99

10+
// PC returns program counters by using runtime.Callers.
11+
func PC(size, skip int) []uintptr {
12+
pc := make([]uintptr, size)
13+
pc = pc[:runtime.Callers(skip, pc)]
14+
return pc
15+
}
16+
1017
func trimSrcPath(s string) string {
1118
var r string
1219
r = strings.TrimPrefix(s, build.Default.GOROOT+"/src/")
@@ -28,9 +35,3 @@ func trimDirs(s string) string {
2835
}
2936
return s
3037
}
31-
32-
func getPC(size, skip int) []uintptr {
33-
pc := make([]uintptr, size)
34-
pc = pc[:runtime.Callers(skip, pc)]
35-
return pc
36-
}

0 commit comments

Comments
 (0)