Skip to content

Commit 4050dd2

Browse files
author
Dean Karn
authored
Merge pull request #8 from go-playground/updates
2 parents 9aa88f6 + b0a3b15 commit 4050dd2

File tree

18 files changed

+196
-211
lines changed

18 files changed

+196
-211
lines changed

Makefile

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
GOCMD=go
1+
GOCMD=GO111MODULE=on go
22

33
linters-install:
4-
@gometalinter --version >/dev/null 2>&1 || { \
4+
@golangci-lint --version >/dev/null 2>&1 || { \
55
echo "installing linting tools..."; \
6-
$(GOCMD) get github.com/alecthomas/gometalinter; \
7-
gometalinter --install; \
6+
$(GOCMD) get github.com/golangci/golangci-lint/cmd/golangci-lint; \
87
}
98

109
lint: linters-install
11-
gometalinter --vendor --disable-all --enable=vet --enable=vetshadow --enable=golint --enable=megacheck --enable=ineffassign --enable=misspell --enable=errcheck --enable=goconst ./...
10+
golangci-lint run
1211

1312
test:
1413
$(GOCMD) test -cover -race ./...

README.md

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package errors
22
============
3-
![Project status](https://img.shields.io/badge/version-3.3.0-green.svg)
3+
![Project status](https://img.shields.io/badge/version-4.0.0-green.svg)
44
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/errors/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/errors)
55
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/errors)](https://goreportcard.com/report/github.com/go-playground/errors)
66
[![GoDoc](https://godoc.org/github.com/go-playground/errors?status.svg)](https://godoc.org/github.com/go-playground/errors)
@@ -13,12 +13,18 @@ stack traces, tags(additional information) and even a Type classification system
1313
Common Questions
1414

1515
Why another package?
16-
Because IMO most of the existing packages either don't take the error handling far enough, too far or down right unfriendly to use/consume.
16+
There are two main reasons.
17+
- I think that the programs generating the original error(s) should be responsible for handling them, even though this package allows access to the original error, and that the callers are mainly interested in:
18+
- If the error is Transient or Permanent for retries.
19+
- Additional details for logging.
20+
21+
- IMO most of the existing packages either don't take the error handling far enough, too far or down right unfriendly to use/consume.
1722

1823
Features
1924
--------
2025
- [x] works with go-playground/log, the Tags will be added as Field Key Values and Types will be concatenated as well when using `WithError`
2126
- [x] helpers to extract and classify error types using `RegisterHelper(...)`, many already existing such as ioerrors, neterrors, awserrors...
27+
- [x] built in helpers only need to be imported, eg. `_ github.com/go-playground/errors/helpers/neterrors` allowing libraries to register their own helpers not needing the caller to do or guess what needs to be imported.
2228

2329
Installation
2430
------------
@@ -44,15 +50,24 @@ func main() {
4450
fmt.Println(err)
4551
if errors.HasType(err, "Permanent") {
4652
// os.Exit(1)
53+
fmt.Println("it is a permanent error")
4754
}
4855

4956
// root error
5057
cause := errors.Cause(err)
51-
fmt.Println(cause)
58+
fmt.Println("CAUSE:", cause)
5259

5360
// can even still inspect the internal error
5461
fmt.Println(errors.Cause(err) == io.EOF) // will extract the cause for you
5562
fmt.Println(errors.Cause(cause) == io.EOF)
63+
64+
// and still in a switch
65+
switch errors.Cause(err) {
66+
case io.EOF:
67+
fmt.Println("EOF error")
68+
default:
69+
fmt.Println("unknown error")
70+
}
5671
}
5772

5873
func level1(value string) error {
@@ -63,7 +78,7 @@ func level1(value string) error {
6378
}
6479

6580
func level2(value string) error {
66-
err := fmt.Errorf("this is an %s", "error")
81+
err := io.EOF
6782
return errors.Wrap(err, "failed to do something").AddTypes("Permanent").AddTags(errors.T("value", value))
6883
}
6984
```
@@ -76,35 +91,23 @@ package main
7691
import (
7792
"fmt"
7893

79-
"strings"
80-
8194
"github.com/go-playground/errors"
8295
)
8396

8497
func main() {
8598
// maybe you just want to grab a stack trace and process on your own like go-playground/log
8699
// uses it to produce a stack trace log message
87100
frame := errors.Stack()
88-
name := fmt.Sprintf("%n", frame)
89-
file := fmt.Sprintf("%+s", frame)
90-
line := fmt.Sprintf("%d", frame)
91-
parts := strings.Split(file, "\n\t")
92-
if len(parts) > 1 {
93-
file = parts[1]
94-
}
101+
fmt.Printf("Function: %s File: %s Line: %d\n", frame.Function(), frame.File(), frame.Line())
95102

96-
fmt.Printf("Name: %s File: %s Line: %s\n", name, file, line)
103+
// and still have access to the underlying runtime.Frame
104+
fmt.Printf("%+v\n", frame.Frame)
97105
}
98106
```
99107

100108
Package Versioning
101109
----------
102-
I'm jumping on the vendoring bandwagon, you should vendor this package as I will not
103-
be creating different version with gopkg.in like allot of my other libraries.
104-
105-
Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE,
106-
it is so freeing not to worry about it and will help me keep pouring out bigger and better
107-
things for you the community.
110+
Using Go modules and proper semantic version releases (as always).
108111

109112
License
110113
------

_examples/basic/main.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,24 @@ func main() {
1212
fmt.Println(err)
1313
if errors.HasType(err, "Permanent") {
1414
// os.Exit(1)
15+
fmt.Println("it is a permanent error")
1516
}
1617

1718
// root error
1819
cause := errors.Cause(err)
19-
fmt.Println(cause)
20+
fmt.Println("CAUSE:", cause)
2021

2122
// can even still inspect the internal error
2223
fmt.Println(errors.Cause(err) == io.EOF) // will extract the cause for you
2324
fmt.Println(errors.Cause(cause) == io.EOF)
25+
26+
// and still in a switch
27+
switch errors.Cause(err) {
28+
case io.EOF:
29+
fmt.Println("EOF error")
30+
default:
31+
fmt.Println("unknown error")
32+
}
2433
}
2534

2635
func level1(value string) error {
@@ -31,6 +40,6 @@ func level1(value string) error {
3140
}
3241

3342
func level2(value string) error {
34-
err := fmt.Errorf("this is an %s", "error")
43+
err := io.EOF
3544
return errors.Wrap(err, "failed to do something").AddTypes("Permanent").AddTags(errors.T("value", value))
3645
}

_examples/helpers/main.go renamed to _examples/helpers/built-in/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import (
55
"net"
66

77
"github.com/go-playground/errors"
8-
"github.com/go-playground/errors/helpers/neterrors"
8+
// init function handles registration automatically
9+
_ "github.com/go-playground/errors/helpers/neterrors"
910
)
1011

1112
func main() {
12-
errors.RegisterHelper(neterrors.NETErrors)
1313
_, err := net.ResolveIPAddr("tcp", "foo")
1414
if err != nil {
1515
err = errors.Wrap(err, "failed to perform operation")

_examples/helpers/custom/main.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net"
6+
7+
"github.com/go-playground/errors"
8+
)
9+
10+
func main() {
11+
errors.RegisterHelper(MyCustomErrHandler)
12+
_, err := net.ResolveIPAddr("tcp", "foo")
13+
if err != nil {
14+
err = errors.Wrap(err, "failed to perform operation")
15+
}
16+
17+
// all that extra context, types and tags captured for free
18+
// there are more helpers and you can even create your own.
19+
fmt.Println(err)
20+
}
21+
22+
// MyCustomErrHandler helps classify my errors
23+
func MyCustomErrHandler(c errors.Chain, err error) (cont bool) {
24+
switch err.(type) {
25+
case net.UnknownNetworkError:
26+
_ = c.AddTypes("io").AddTag("additional", "details")
27+
return
28+
//case net.Other:
29+
// ...
30+
// return
31+
}
32+
return true
33+
}

_examples/stack/main.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,15 @@ package main
33
import (
44
"fmt"
55

6-
"strings"
7-
86
"github.com/go-playground/errors"
97
)
108

119
func main() {
1210
// maybe you just want to grab a stack trace and process on your own like go-playground/log
1311
// uses it to produce a stack trace log message
1412
frame := errors.Stack()
15-
name := fmt.Sprintf("%n", frame)
16-
file := fmt.Sprintf("%+s", frame)
17-
line := fmt.Sprintf("%d", frame)
18-
parts := strings.Split(file, "\n\t")
19-
if len(parts) > 1 {
20-
file = parts[1]
21-
}
13+
fmt.Printf("Function: %s File: %s Line: %d\n", frame.Function(), frame.File(), frame.Line())
2214

23-
fmt.Printf("Name: %s File: %s Line: %s\n", name, file, line)
15+
// and still have access to the underlying runtime.Frame
16+
fmt.Printf("%+v\n", frame.Frame)
2417
}

benchmarks_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package errors
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func BenchmarkError(b *testing.B) {
8+
err := New("base error")
9+
for i := 0; i < b.N; i++ {
10+
_ = err.Error()
11+
}
12+
}
13+
14+
func BenchmarkErrorParallel(b *testing.B) {
15+
err := New("base error")
16+
b.RunParallel(func(pb *testing.PB) {
17+
for pb.Next() {
18+
_ = err.Error()
19+
}
20+
})
21+
}

chain.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package errors
22

33
import (
44
"fmt"
5+
"strconv"
56
"strings"
67
)
78

@@ -10,7 +11,7 @@ func T(key string, value interface{}) Tag {
1011
return Tag{Key: key, Value: value}
1112
}
1213

13-
// Tag contains a single key value conbination
14+
// Tag contains a single key value combination
1415
// to be attached to your error
1516
type Tag struct {
1617
Key string
@@ -21,7 +22,7 @@ func newLink(err error, prefix string, skipFrames int) *Link {
2122
return &Link{
2223
Err: err,
2324
Prefix: prefix,
24-
Source: st(skipFrames),
25+
Source: StackLevel(skipFrames),
2526
}
2627

2728
}
@@ -31,13 +32,16 @@ type Chain []*Link
3132

3233
// Error returns the formatted error string
3334
func (c Chain) Error() string {
34-
lines := make([]string, 0, len(c))
35+
b := make([]byte, 0, len(c)*128)
36+
3537
//source=<source> <prefix>: <error> tag=value tag2=value2 types=type1,type2
3638
for i := len(c) - 1; i >= 0; i-- {
37-
line := c[i].formatError()
38-
lines = append(lines, line)
39+
b = c[i].formatError(b)
40+
if i > 0 {
41+
b = append(b, '\n')
42+
}
3943
}
40-
return strings.Join(lines, "\n")
44+
return string(b)
4145
}
4246

4347
// Link contains a single error entry, unless it's the top level error, in
@@ -57,32 +61,41 @@ type Link struct {
5761
Tags []Tag
5862

5963
// Source contains the name, file and lines obtained from the stack trace
60-
Source string
64+
Source Frame
6165
}
6266

6367
// formatError prints a single Links error
64-
func (l *Link) formatError() string {
65-
line := fmt.Sprintf("source=%s ", l.Source)
68+
func (l *Link) formatError(b []byte) []byte {
69+
b = append(b, "source="...)
70+
b = append(b, l.Source.Function()...)
71+
b = append(b, ": "...)
72+
b = append(b, l.Source.File()...)
73+
b = append(b, ':')
74+
strconv.AppendInt(b, int64(l.Source.Line()), 10)
6675

6776
if l.Prefix != "" {
68-
line += l.Prefix
77+
b = append(b, l.Prefix...)
6978
}
7079

7180
if _, ok := l.Err.(Chain); !ok {
7281
if l.Prefix != "" {
73-
line += ": "
82+
b = append(b, ": "...)
7483
}
75-
line += l.Err.Error()
84+
b = append(b, l.Err.Error()...)
7685
}
7786

7887
for _, tag := range l.Tags {
79-
line += fmt.Sprintf(" %s=%v", tag.Key, tag.Value)
88+
b = append(b, ' ')
89+
b = append(b, tag.Key...)
90+
b = append(b, '=')
91+
b = append(b, fmt.Sprintf("%v", tag.Value)...)
8092
}
8193

8294
if len(l.Types) > 0 {
83-
line += " types=" + strings.Join(l.Types, ",")
95+
b = append(b, " types="...)
96+
b = append(b, strings.Join(l.Types, ",")...)
8497
}
85-
return line
98+
return b
8699
}
87100

88101
// helper method to get the current *Link from the top level
@@ -114,5 +127,5 @@ func (c Chain) AddTypes(typ ...string) Chain {
114127

115128
// Wrap adds another contextual prefix to the error chain
116129
func (c Chain) Wrap(prefix string) Chain {
117-
return wrap(c, prefix, 0)
130+
return wrap(c, prefix, 3)
118131
}

0 commit comments

Comments
 (0)