Skip to content

Commit 897d503

Browse files
committed
[patch] replaced fmt.Sprintf with + concatenation for best performance
[-] added benchmark details
1 parent ba29500 commit 897d503

File tree

3 files changed

+80
-5
lines changed

3 files changed

+80
-5
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Errors package is a drop-in replacement of the built-in Go errors package with n
1313
1. Multiple (11) error types
1414
2. User friendly message
1515
3. File & line number prefixed to errors
16+
4. HTTP status code and user friendly message (wrapped messages are concatenated) for all error types
1617

1718
In case of nested errors, the messages (in case of nesting with this package's error) & errors are also looped through.
1819

@@ -75,6 +76,11 @@ func main() {
7576

7677
A common annoyance with Go errors which most people are aware of is, figuring out the origin of the error, especially when there are nested function calls. Ever since error annotation was introduced in Go, a lot of people have tried using it to trace out an errors origin by giving function names, contextual message etc in it. e.g. `fmt.Errorf("database query returned error %w", err)`. This errors package, whenever you call the Go error interface's `Error() string` function, it'll print the error prefixed by the filepath and line number. It'd look like `../Users/JohnDoe/apps/main.go:50 hello world` where 'hello world' is the error message.
7778

79+
### HTTP status code & message
80+
81+
The function `errors.HTTPStatusCodeMessage(error) (int, string, bool)` returns the HTTP status code, message, and a boolean value. The boolean if true, means the error is of type *Error from this package.
82+
If error is nested with multiple errors, it loops through all the levels and returns a single concatenated message. This is illustrated in the 'How to use?' section
83+
7884
## How to use?
7985

8086
Before that, over the years I have tried error with stack trace, annotation, custom error package with error codes etc. Finally, I think this package gives the best of all worlds, for most generic usecases.
@@ -169,10 +175,31 @@ And output of the `fmt.Println(err.Error())`
169175
```bash
170176
/Users/username/go/src/errorscheck/main.go:28 /Users/username/go/src/errorscheck/main.go:20 sinking bar
171177
```
178+
## Benchmark
179+
180+
Benchmark run on:
181+
<p><img width="320" alt="Screenshot 2020-07-18 at 6 25 22 PM" src="https://user-images.githubusercontent.com/1092882/87852981-241b5c80-c924-11ea-9d22-296acdead7cc.png"></p>
182+
183+
Results
184+
```bash
185+
$ go version
186+
go version go1.14.4 darwin/amd64
187+
$ go test -bench=.
188+
goos: darwin
189+
goarch: amd64
190+
pkg: github.com/bnkamalesh/errors
191+
Benchmark_Internal-8 1880440 635 ns/op
192+
Benchmark_InternalErr-8 1607589 746 ns/op
193+
Benchmark_InternalGetError-8 1680831 711 ns/op
194+
Benchmark_InternalGetErrorWithNestedError-8 1462900 823 ns/op
195+
Benchmark_InternalGetMessage-8 1859172 646 ns/op
196+
Benchmark_InternalGetMessageWithNestedError-8 1649259 726 ns/op
197+
Benchmark_HTTPStatusCodeMessage-8 23497728 50.3 ns/op
198+
```
172199

173200
## Contributing
174201

175-
If more error types, customization etc. are required, PRs & issues are welcome!
202+
More error types, customization etc; PRs & issues are welcome!
176203

177204
## The gopher
178205

errors.go

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

44
import (
5-
"fmt"
65
"net/http"
76
"runtime"
87
"strings"
@@ -62,7 +61,8 @@ type Error struct {
6261
// Error is the implementation of error interface
6362
func (e *Error) Error() string {
6463
if e.original != nil {
65-
return fmt.Sprintf("%s %s", e.fileLine, e.original.Error())
64+
// string concatenation with + is ~100x faster than fmt.Sprintf()
65+
return e.fileLine + " " + e.original.Error()
6666
/*
6767
// use the following code instead of the above return, to avoid nested filename:line
6868
err, _ := e.original.(*Error)
@@ -74,10 +74,12 @@ func (e *Error) Error() string {
7474
}
7575

7676
if e.message != "" {
77-
return fmt.Sprintf("%s %s", e.fileLine, e.message)
77+
// string concatenation with + is ~100x faster than fmt.Sprintf()
78+
return e.fileLine + " " + e.message
7879
}
7980

80-
return fmt.Sprintf("%s %s", e.fileLine, DefaultMessage)
81+
// string concatenation with + is ~100x faster than fmt.Sprintf()
82+
return e.fileLine + " " + DefaultMessage
8183
}
8284

8385
// Message returns the user friendly message stored in the error struct

errors_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,49 @@ func TestSetDefaultType(t *testing.T) {
174174
})
175175
}
176176
}
177+
178+
func Benchmark_Internal(b *testing.B) {
179+
for i := 0; i < b.N; i++ {
180+
Internal("hello world")
181+
}
182+
}
183+
184+
func Benchmark_InternalErr(b *testing.B) {
185+
err := errors.New("bad error")
186+
for i := 0; i < b.N; i++ {
187+
InternalErr(err, "hello world")
188+
}
189+
}
190+
191+
func Benchmark_InternalGetError(b *testing.B) {
192+
for i := 0; i < b.N; i++ {
193+
_ = Internal("hello world").Error()
194+
}
195+
}
196+
func Benchmark_InternalGetErrorWithNestedError(b *testing.B) {
197+
err := errors.New("bad error")
198+
for i := 0; i < b.N; i++ {
199+
_ = InternalErr(err, "hello world").Error()
200+
}
201+
}
202+
203+
func Benchmark_InternalGetMessage(b *testing.B) {
204+
for i := 0; i < b.N; i++ {
205+
_ = Internal("hello world").Message()
206+
}
207+
}
208+
209+
func Benchmark_InternalGetMessageWithNestedError(b *testing.B) {
210+
err := New("bad error")
211+
for i := 0; i < b.N; i++ {
212+
_ = InternalErr(err, "hello world").Message()
213+
}
214+
}
215+
216+
func Benchmark_HTTPStatusCodeMessage(b *testing.B) {
217+
// SubscriptionExpiredErr is the slowest considering it's the last item in switch case
218+
err := SubscriptionExpiredErr(SubscriptionExpired("old"), "expired")
219+
for i := 0; i < b.N; i++ {
220+
_, _, _ = HTTPStatusCodeMessage(err)
221+
}
222+
}

0 commit comments

Comments
 (0)