Skip to content

Commit f5c8af6

Browse files
authored
Merge pull request #2 from efimovalex/Improvements
Improvements: Added context to each error stack and IsNotFound function
2 parents 147b154 + 4797298 commit f5c8af6

File tree

8 files changed

+178
-62
lines changed

8 files changed

+178
-62
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@
1212

1313
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
1414
.glide/
15+
16+
coverage.txt

.travis.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ go:
1111
before_install:
1212
- go get -t -v ./...
1313

14-
script:
15-
- go test -v ./...
16-
1714
script:
1815
- go test -race -coverprofile=coverage.txt -covermode=atomic
1916

README.md

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
[![Build Status](https://travis-ci.org/efimovalex/stackerr.svg?branch=master)](https://travis-ci.org/efimovalex/stackerr)
33
[![Go Report Card](https://goreportcard.com/badge/github.com/efimovalex/stackerr)](https://goreportcard.com/report/github.com/efimovalex/stackerr) [![codecov](https://codecov.io/gh/efimovalex/stackerr/branch/master/graph/badge.svg)](https://codecov.io/gh/efimovalex/stackerr) [![GoDoc](https://godoc.org/github.com/efimovalex/stackerr?status.svg)](https://godoc.org/github.com/efimovalex/stackerr)
44

5-
An error implementation with StatusCode and Stacktrace
5+
An error implementation with StatusCode and Stacktrace
6+
7+
It implements the Golang error interface
8+
9+
You can use Status Code to better identify the answer you need to report to the client.
10+
11+
Makes debugging easier by logging the functions the error passes through and by adding the ability to log context on each function pass of the error, so that you can create a path of the error through your application.
612

713
## Install
814

@@ -15,17 +21,21 @@ $ go get github.com/efimovalex/stackerr
1521
```Go
1622
package main
1723

18-
import "github.com/efimovalex/stackerr"
19-
import "fmt"
24+
import (
25+
"fmt"
26+
"net/http"
27+
28+
"github.com/efimovalex/stackerr"
29+
)
2030

2131
func f1() *stackerr.Err {
22-
err := stackerr.Error("message")
32+
err := stackerr.NewWithStatusCode("message", http.StatusNotFound)
2333
return err.Stack()
2434
}
2535

2636
func f2() *stackerr.Err {
2737
err := f1()
28-
return err.Stack()
38+
return err.StackWithContext("context")
2939
}
3040

3141
type t1 struct{}
@@ -43,25 +53,36 @@ func main() {
4353

4454
fmt.Println(err.Error())
4555

56+
fmt.Println(err.StatusCode)
57+
58+
if err.IsNotFound() {
59+
fmt.Println("Resource is not found")
60+
}
61+
4662
err.Log()
4763
}
64+
4865
```
4966
Output:
5067

5168
```console
5269
Error Stacktrace:
53-
-> github.com/efimovalex/stackerr/example/main.go:25 (main.main)
54-
-> github.com/efimovalex/stackerr/example/main.go:19 (main.(*t1).f3)
55-
-> github.com/efimovalex/stackerr/example/main.go:12 (main.f2)
56-
-> github.com/efimovalex/stackerr/example/main.go:7 (main.f1)
70+
-> github.com/efimovalex/stackerr/example/main.go:29 (main.main)
71+
-> github.com/efimovalex/stackerr/example/main.go:23 (main.(*t1).f3)
72+
-> github.com/efimovalex/stackerr/example/main.go:16 (main.f2) context
73+
-> github.com/efimovalex/stackerr/example/main.go:11 (main.f1)
5774

5875
message
5976

60-
2017/08/31 12:13:47 Error Stacktrace:
61-
-> github.com/efimovalex/stackerr/example/main.go:25 (main.main)
62-
-> github.com/efimovalex/stackerr/example/main.go:19 (main.(*t1).f3)
63-
-> github.com/efimovalex/stackerr/example/main.go:12 (main.f2)
64-
-> github.com/efimovalex/stackerr/example/main.go:7 (main.f1)
77+
404
78+
79+
Resource is not found
80+
81+
2017/09/05 17:32:15 Error Stacktrace:
82+
-> github.com/efimovalex/stackerr/example/main.go:29 (main.main)
83+
-> github.com/efimovalex/stackerr/example/main.go:23 (main.(*t1).f3)
84+
-> github.com/efimovalex/stackerr/example/main.go:16 (main.f2) context
85+
-> github.com/efimovalex/stackerr/example/main.go:11 (main.f1)
6586
```
6687
## Authors
6788

example/main.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package main
22

3-
import "github.com/efimovalex/stackerr"
4-
import "fmt"
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/efimovalex/stackerr"
8+
)
59

610
func f1() *stackerr.Err {
7-
err := stackerr.Error("message")
11+
err := stackerr.NewWithStatusCode("message", http.StatusNotFound)
812
return err.Stack()
913
}
1014

1115
func f2() *stackerr.Err {
1216
err := f1()
13-
return err.Stack()
17+
return err.StackWithContext("context")
1418
}
1519

1620
type t1 struct{}
@@ -28,5 +32,15 @@ func main() {
2832

2933
fmt.Println(err.Error())
3034

35+
fmt.Println(err.StatusCode)
36+
37+
if err.IsNotFound() {
38+
fmt.Println("Resource is not found")
39+
}
40+
41+
newErr := stackerr.NewFromError(err)
42+
3143
err.Log()
44+
45+
newErr.Log()
3246
}

goerr.go

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package stackerr
33
import (
44
"fmt"
55
"log"
6+
"net/http"
67
"os"
78
"runtime"
89
"strings"
@@ -21,24 +22,42 @@ func (e *Err) Error() string {
2122
return e.Message
2223
}
2324

24-
// Error - creates a new Err struct
25-
func Error(message string) *Err {
25+
// New - creates a new Err struct with 500 Error Code
26+
func New(message string) *Err {
2627
err := Err{
27-
Message: message,
28+
Message: message,
29+
StatusCode: http.StatusInternalServerError,
2830
}
31+
2932
return err.Stack()
3033
}
3134

32-
// ErrorWS - creates a new Err struct
33-
func ErrorWS(message string, statusCode int) *Err {
35+
// NewWithStatusCode - creates a new Err struct with a custom Error Code
36+
func NewWithStatusCode(message string, statusCode int) *Err {
3437
err := Err{
3538
Message: message,
3639
StatusCode: statusCode,
3740
}
41+
42+
return err.Stack()
43+
}
44+
45+
// NewFromError - creates a new Err struct with a custom Error Code
46+
func NewFromError(e error) *Err {
47+
err := Err{
48+
Message: e.Error(),
49+
StatusCode: http.StatusInternalServerError,
50+
}
51+
3852
return err.Stack()
3953
}
4054

41-
// Stack - Adds the current function and link to the previous Stacktrace
55+
// IsNotFound - return true if the error type is resource not found.
56+
func (e *Err) IsNotFound() bool {
57+
return e.StatusCode == http.StatusNotFound
58+
}
59+
60+
// Stack - Adds the current function to Err and a link to the previous Stacktrace
4261
func (e *Err) Stack() *Err {
4362
pc := make([]uintptr, 10) // at least 1 entry needed
4463
runtime.Callers(2, pc)
@@ -49,7 +68,23 @@ func (e *Err) Stack() *Err {
4968
file = strings.Replace(file, os.Getenv("GOPATH")+"/src/", "", -1)
5069
}
5170

52-
e.stacktrace = &Stack{File: file, Function: getFunctionName(f.Name()), Line: line, CallbackStack: e.stacktrace}
71+
e.stacktrace = &Stack{File: file, Function: getFunctionName(f), Line: line, CallbackStack: e.stacktrace}
72+
e.stackCount++
73+
return e
74+
}
75+
76+
// StackWithContext - Adds the current function and context to Err and a link to the previous Stacktrace
77+
func (e *Err) StackWithContext(context string) *Err {
78+
pc := make([]uintptr, 10) // at least 1 entry needed
79+
runtime.Callers(2, pc)
80+
f := runtime.FuncForPC(pc[1])
81+
file, line := f.FileLine(pc[1])
82+
83+
if os.Getenv("GOPATH") != "" {
84+
file = strings.Replace(file, os.Getenv("GOPATH")+"/src/", "", -1)
85+
}
86+
e.stacktrace.Context = context
87+
e.stacktrace = &Stack{File: file, Function: getFunctionName(f), Line: line, CallbackStack: e.stacktrace}
5388
e.stackCount++
5489
return e
5590
}
@@ -69,8 +104,8 @@ func (e *Err) Log() {
69104
log.Print(e.stacktrace.Sprint())
70105
}
71106

72-
func getFunctionName(name string) string {
73-
parts := strings.Split(name, "/")
107+
func getFunctionName(f *runtime.Func) string {
108+
parts := strings.Split(f.Name(), "/")
74109

75110
return parts[len(parts)-1]
76111
}

goerr_test.go

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@ package stackerr
22

33
import (
44
"bytes"
5+
"errors"
6+
"io"
57
"log"
8+
"net/http"
69
"os"
710
"testing"
811

912
"github.com/stretchr/testify/assert"
1013
)
1114

1215
func f1() *Err {
13-
err := Error("message")
16+
err := New("message")
1417
return err.Stack()
1518
}
1619

1720
func f2() *Err {
1821
err := f1()
19-
return err.Stack()
22+
return err.StackWithContext("context")
2023
}
2124

2225
type t1 struct{}
@@ -25,44 +28,50 @@ func (t *t1) f3() *Err {
2528
err := f2()
2629
return err.Stack()
2730
}
31+
2832
func TestStackTrace(t *testing.T) {
2933
ts := t1{}
3034
err := ts.f3()
3135

3236
assert.NotNil(t, err)
33-
assert.Equal(t, err.Error(), "message")
34-
assert.Equal(t, err.Sprint(),
37+
assert.Equal(t, "message", err.Error())
38+
assert.Equal(t,
3539
`Error Stacktrace:
36-
-> github.com/efimovalex/stackerr/goerr_test.go:30 (stackerr.TestStackTrace)
37-
-> github.com/efimovalex/stackerr/goerr_test.go:25 (stackerr.(*t1).f3)
38-
-> github.com/efimovalex/stackerr/goerr_test.go:18 (stackerr.f2)
39-
-> github.com/efimovalex/stackerr/goerr_test.go:13 (stackerr.f1)
40-
`)
40+
-> github.com/efimovalex/stackerr/goerr_test.go:34 (stackerr.TestStackTrace)
41+
-> github.com/efimovalex/stackerr/goerr_test.go:28 (stackerr.(*t1).f3)
42+
-> github.com/efimovalex/stackerr/goerr_test.go:21 (stackerr.f2) context
43+
-> github.com/efimovalex/stackerr/goerr_test.go:16 (stackerr.f1)
44+
`, err.Sprint())
4145
}
4246

43-
func TestError(t *testing.T) {
44-
err := Error("message")
47+
func TestNew(t *testing.T) {
48+
err := New("message")
4549

4650
assert.NotNil(t, err)
47-
assert.Equal(t, err.Error(), "message")
48-
expected := Err{
49-
Message: "message",
50-
}
51+
assert.Equal(t, "message", err.Error())
52+
assert.Equal(t, http.StatusInternalServerError, err.StatusCode)
53+
}
54+
55+
func TestNewFromError(t *testing.T) {
56+
e := errors.New("message")
57+
err := NewFromError(e)
5158

52-
assert.Equal(t, err.Error(), expected.Error())
59+
assert.NotNil(t, err)
60+
assert.Equal(t, "message", err.Error())
61+
assert.Equal(t, http.StatusInternalServerError, err.StatusCode)
62+
63+
errErr := NewFromError(err)
64+
assert.NotNil(t, errErr)
65+
assert.Equal(t, "message", errErr.Error())
66+
assert.Equal(t, http.StatusInternalServerError, errErr.StatusCode)
5367
}
54-
func TestErrorWS(t *testing.T) {
55-
err := ErrorWS("message", 200)
68+
69+
func TestNewWithStatusCode(t *testing.T) {
70+
err := NewWithStatusCode("message", http.StatusOK)
5671

5772
assert.NotNil(t, err)
58-
assert.Equal(t, err.Error(), "message")
59-
expected := Err{
60-
Message: "message",
61-
StatusCode: 200,
62-
}
63-
64-
assert.Equal(t, err.Error(), expected.Error())
65-
assert.Equal(t, err.StatusCode, expected.StatusCode)
73+
assert.Equal(t, "message", err.Error())
74+
assert.Equal(t, http.StatusOK, err.StatusCode)
6675
}
6776
func TestLog(t *testing.T) {
6877
var buf bytes.Buffer
@@ -72,8 +81,45 @@ func TestLog(t *testing.T) {
7281
log.SetOutput(os.Stderr)
7382
assert.Contains(t, buf.String(),
7483
`Error Stacktrace:
75-
-> github.com/efimovalex/stackerr/goerr_test.go:70 (stackerr.TestLog)
76-
-> github.com/efimovalex/stackerr/goerr_test.go:18 (stackerr.f2)
77-
-> github.com/efimovalex/stackerr/goerr_test.go:13 (stackerr.f1)
84+
-> github.com/efimovalex/stackerr/goerr_test.go:79 (stackerr.TestLog)
85+
-> github.com/efimovalex/stackerr/goerr_test.go:21 (stackerr.f2) context
86+
-> github.com/efimovalex/stackerr/goerr_test.go:16 (stackerr.f1)
87+
`)
88+
}
89+
90+
func TestIsNotFound(t *testing.T) {
91+
err := NewWithStatusCode("message", http.StatusOK)
92+
93+
assert.False(t, err.IsNotFound())
94+
95+
errNotFound := NewWithStatusCode("message", http.StatusNotFound)
96+
97+
assert.True(t, errNotFound.IsNotFound())
98+
}
99+
100+
func TestPrint(t *testing.T) {
101+
old := os.Stdout // keep backup of the real stdout
102+
r, w, _ := os.Pipe()
103+
os.Stdout = w
104+
105+
outC := make(chan string)
106+
// copy the output in a separate goroutine so printing can't block indefinitely
107+
go func() {
108+
var buf bytes.Buffer
109+
io.Copy(&buf, r)
110+
outC <- buf.String()
111+
}()
112+
err := f2()
113+
err.Print()
114+
115+
w.Close()
116+
os.Stdout = old // restoring the real stdout
117+
out := <-outC
118+
119+
assert.Equal(t, out,
120+
`Error Stacktrace:
121+
-> github.com/efimovalex/stackerr/goerr_test.go:112 (stackerr.TestPrint)
122+
-> github.com/efimovalex/stackerr/goerr_test.go:21 (stackerr.f2) context
123+
-> github.com/efimovalex/stackerr/goerr_test.go:16 (stackerr.f1)
78124
`)
79125
}

0 commit comments

Comments
 (0)