Created
April 11, 2019 09:32
-
-
Save shovanmaity/bd5b6317d9fc9ab9df810cb79e641a1a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"log" | |
"github.com/pkg/errors" | |
) | |
func jsonIndent(ctx string, obj interface{}) string { | |
if obj == nil { | |
return "" | |
} | |
b, _ := json.MarshalIndent(obj, "", ".") | |
return fmt.Sprintf("%s %s", ctx, string(b)) | |
} | |
func newTypea() *Typea { | |
return &Typea{Item: 1} | |
} | |
func newTypeb() *Typeb { | |
return &Typeb{Item: 2} | |
} | |
func newTypec() *Typec { | |
return &Typec{Item: 3} | |
} | |
// Struct typea with GoString and String implementation | |
type Typea struct { | |
Item int | |
} | |
func (a *Typea) String() string { | |
return jsonIndent("struct Typea", a) | |
} | |
func (a *Typea) GoString() string { | |
return a.String() | |
} | |
func (a *Typea) errorFunc1() error { | |
b := newTypeb() | |
return b.errorFunc1() | |
} | |
func (a *Typea) errorFunc2() error { | |
b := newTypeb() | |
return errors.Wrapf(b.errorFunc2(), "object: %+v", a) | |
} | |
func (a *Typea) errorFunc3() error { | |
b := newTypeb() | |
return errors.WithMessagef(b.errorFunc3(), "object: %+v", a) | |
} | |
func (a *Typea) errorFunc4() error { | |
b := newTypeb() | |
return errors.WithMessagef(b.errorFunc4(), "object: %+v", a) | |
} | |
func (a *Typea) errorFunc5() error { | |
b := newTypeb() | |
return errors.WithMessagef(b.errorFunc5(), "object: %+v", a) | |
} | |
// Struct typeb with GoString and String implementation | |
type Typeb struct { | |
Item int | |
} | |
func (b *Typeb) String() string { | |
return jsonIndent("struct Typeb", b) | |
} | |
func (b *Typeb) GoString() string { | |
return b.String() | |
} | |
func (b *Typeb) errorFunc1() error { | |
c := newTypec() | |
return c.errorFunc1() | |
} | |
func (b *Typeb) errorFunc2() error { | |
c := newTypec() | |
return errors.Wrapf(c.errorFunc1(), "object: %+v", b) | |
} | |
func (b *Typeb) errorFunc3() error { | |
c := newTypec() | |
return errors.WithMessagef(c.errorFunc1(), "object: %+v", b) | |
} | |
func (b *Typeb) errorFunc4() error { | |
c := newTypec() | |
return errors.WithMessagef(c.errorFunc2(), "object: %+v", b) | |
} | |
func (b *Typeb) errorFunc5() error { | |
c := newTypec() | |
return errors.WithMessagef(c.errorFunc3(), "object: %+v", b) | |
} | |
// Struct typec with GoString and String implementation | |
type Typec struct { | |
Item int | |
} | |
func (c *Typec) String() string { | |
return jsonIndent("struct Typec", c) | |
} | |
func (c *Typec) GoString() string { | |
return c.String() | |
} | |
func (c *Typec) errorFunc1() error { | |
return errors.Errorf("new error %+v", c) | |
} | |
func (c *Typec) errorFunc2() error { | |
return fmt.Errorf("new error %+v", c) | |
} | |
func (c *Typec) errorFunc3() error { | |
err := fmt.Errorf("new error") | |
// return errors.Wrapf(err, "%+v", c) | |
return errors.Errorf("%s %+v", err, c) | |
} | |
// main function | |
func main() { | |
a := newTypea() | |
log.Printf(`------------------------------Example-1------------------------------ | |
This is a basic example of printing StackTrace. I have 4 functions main, func1, func2, | |
func3. Here source of error(func3) returns an error and func2 and func3 returns same error. | |
func func3() err{ | |
return errors.New("new error") | |
} | |
func func2() err{ | |
return func3() | |
} | |
func func1() err{ | |
return func2() | |
} | |
func main(){ | |
log.Println(func1()) | |
} | |
--------------------------------------------------------------------------------------------`) | |
log.Printf("\n\n\nerror: %+v\n\n\n", a.errorFunc1()) | |
log.Printf(`------------------------------Example-2------------------------------ | |
Now question how can I add some more extra details in every function calls? | |
=> we can use errors.Wraf to add more info in every function calls. | |
func func3() err{ | |
return errors.New("new error") | |
} | |
func func2() err{ | |
return errors.Wraf(func3(),"function2") | |
} | |
func func1() err{ | |
return errors.Wraf(func2(),"function1") | |
} | |
func main(){ | |
log.Println(func1()) | |
} | |
--------------------------------------------------------------------------------------------`) | |
log.Printf("\n\n\nerror: %+v\n\n", a.errorFunc2()) | |
log.Printf(`------------------------------Example-3------------------------------ | |
Now question my StackTrace is printing multiple time. why and how can I avoid this? | |
Why? | |
=> errors.Wrapf creates a new instance of error also new StackTrace for it. Thats why we | |
are getting StackTrace multiple times. | |
How to avoid? | |
=> we can use errors.WithMessagef to avoid this. errors.WithMessagef will add only extra | |
message in that error. | |
func func3() err{ | |
return errors.New("new error") | |
} | |
func func2() err{ | |
return errors.WithMessagef(func3(),"function2") | |
} | |
func func1() err{ | |
return errors.WithMessagef(func2(),"function1") | |
} | |
func main(){ | |
log.Println(func1()) | |
} | |
--------------------------------------------------------------------------------------------`) | |
log.Printf("\n\n\nerror: %+v\n\n\n", a.errorFunc3()) | |
log.Printf(`------------------------------Example-4------------------------------ | |
Now question what happen if my error is generated by some external package and it | |
does not have StackTrace. | |
=> It will not print StackTrace | |
func func3() err{ | |
return fmt.Errorf("new error") | |
} | |
func func2() err{ | |
return errors.WithMessagef(func3(),"function2") | |
} | |
func func1() err{ | |
return errors.WithMessagef(func2(),"function1") | |
} | |
func main(){ | |
log.Println(func1()) | |
} | |
--------------------------------------------------------------------------------------------`) | |
log.Printf("\n\n\nerror: %+v\n\n\n", a.errorFunc4()) | |
log.Printf(`------------------------------Example-5------------------------------ | |
Now question How to solve this? | |
=> Wraping external error will solve this problem | |
func func3() err{ | |
return errors.Wrapf(fmt.Errorf("new error"),"function3") | |
} | |
func func2() err{ | |
return errors.WithMessagef(func3(),"function2") | |
} | |
func func1() err{ | |
return errors.WithMessagef(func2(),"function1") | |
} | |
func main(){ | |
log.Println(func1()) | |
} | |
--------------------------------------------------------------------------------------------`) | |
log.Printf("\n\n\nerror: %+v\n\n\n", a.errorFunc5()) | |
log.Println(` | |
----------------------------------------------------------------------------------------- | |
Q- how we are printing StackTrace? | |
=> Here I copied some comments from github.com/pkg/errors package. | |
All error values returned from this package implement fmt.Formatter and can | |
be formatted by the fmt package. The following verbs are supported: | |
%s => print the error. If the error has a Cause it will be printed recursively. | |
%v => see %s | |
%+v => extended format. Each Frame of the error's StackTrace will be printed in detail. | |
----------------------------------------------------------------------------------------- | |
Q- What does errors.WithMessagef() do? | |
=> Here I copied some comments and code from github.com/pkg/errors package. | |
type withMessage struct { | |
cause error | |
msg string | |
} | |
func (w *withMessage) Cause() error { return w.cause } | |
// WithMessagef annotates err with the format specifier. | |
func WithMessagef(err error, format string, args ...interface{}) error { | |
if err == nil { | |
return nil | |
} | |
return &withMessage{ | |
cause: err, | |
msg: fmt.Sprintf(format, args...), | |
} | |
} | |
----------------------------------------------------------------------------------------- | |
Q- What does errors.Wrapf() do? | |
=> Here I copied some comments and code from github.com/pkg/errors package. | |
type withStack struct { | |
error | |
*stack | |
} | |
func (w *withStack) Cause() error { return w.error } | |
// Wrapf returns an error annotating err with a stack trace | |
// at the point Wrapf is called, and the format specifier. | |
func Wrapf(err error, format string, args ...interface{}) error { | |
if err == nil { | |
return nil | |
} | |
err = &withMessage{ | |
cause: err, | |
msg: fmt.Sprintf(format, args...), | |
} | |
return &withStack{ | |
err, | |
callers(), | |
} | |
} | |
-----------------------------------------------------------------------------------------`) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment