Last active
October 11, 2016 05:28
-
-
Save corebreaker/1eb284ddea2af1896c57bf52c56499da to your computer and use it in GitHub Desktop.
Golang: Verbose errors with stacktraces
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
/* | |
Example: | |
-------- | |
import ( | |
"os" | |
"log" | |
"io/ioutil" | |
"verbose_errors" | |
) | |
func read(name string) ([]data, error) { | |
res, err := ioutil.ReadFile(name) | |
return res, verbose_errors.WrapError(err) | |
} | |
func file_util(name string) error { | |
data, err := read(name) | |
if err != nil { | |
return err | |
} | |
content := string(data) | |
size := len(content) | |
if size < 50 { | |
return verbose_errors.MakeError("File with less than 50 characters") | |
} | |
log.Printf("Read file %s: %s...", name, content[:40]) | |
return nil | |
} | |
func main() { | |
err := file_util(os.Args[1]) | |
if err != nil { | |
log.Fatal(err) // Show error with stack trace | |
} | |
} | |
*/ | |
package verbose_errors | |
import ( | |
"bytes" | |
"errors" | |
"fmt" | |
"runtime" | |
) | |
// | |
/* ------------- Interfaces ------------------------------------------------ */ | |
type StackEntry interface { | |
fmt.Stringer | |
GetName() string | |
GetFile() string | |
GetLine() uint | |
} | |
// | |
/* ------------- Stack entry implementation -------------------------------- */ | |
type tStackEnry struct { | |
name string | |
file string | |
line uint | |
} | |
func (self *tStackEnry) GetName() string { return self.name } | |
func (self *tStackEnry) GetFile() string { return self.file } | |
func (self *tStackEnry) GetLine() uint { return self.line } | |
func (self *tStackEnry) String() string { | |
return fmt.Sprintf("%s (%s:%d)", self.name, self.file, self.line) | |
} | |
// | |
/* ------------- Error wrapping -------------------------------------------- */ | |
type tVerboseError struct { | |
source error | |
message string | |
trace []StackEntry | |
} | |
func (self *tVerboseError) Error() string { | |
var out bytes.Buffer | |
fmt.Fprintln(&out, self.source.Error()) | |
fmt.Fprint(&out, self.message) | |
for _, line := range self.trace { | |
fmt.Fprintln(&out, " ", line) | |
} | |
if len(self.trace) > 0 { | |
fmt.Fprintln(&out, "------------------------------------------------------------------------------") | |
} | |
return out.String() | |
} | |
func (self *tVerboseError) add_info(format string, args ...interface{}) error { | |
var out bytes.Buffer | |
fmt.Fprintln(&out, fmt.Sprintf(format, args...)) | |
self.message += out.String() | |
return self | |
} | |
func wrap_error(err error) *tVerboseError { | |
if err == nil { | |
return nil | |
} | |
var pc uintptr = 1 | |
stack := make([]StackEntry, 0) | |
for i := 1; pc != 0; i++ { | |
ptr, file, line, ok := runtime.Caller(i) | |
pc = ptr | |
if (pc == 0) || (!ok) { | |
continue | |
} | |
f := runtime.FuncForPC(pc) | |
stack = append(stack, &tStackEnry{name: f.Name(), file: file, line: uint(line)}) | |
} | |
return &tVerboseError{ | |
source: err, | |
trace: stack, | |
} | |
} | |
// | |
/*------------- Public APIs ----------------------------------------------- */ | |
func WrapError(err error) error { | |
return wrap_error(err) | |
} | |
func AddErrorInfo(err error, format string, args ...interface{}) error { | |
verbose_error, ok := err.(*tVerboseError) | |
if !ok { | |
verbose_error = wrap_error(err) | |
} | |
return verbose_error.add_info(format, args...) | |
} | |
func GetSource(err error) error { | |
verbose_error, ok := err.(*tVerboseError) | |
if !ok { | |
return err | |
} | |
return verbose_error.source | |
} | |
func GetStackTrace(err error) []StackEntry { | |
verbose_error, ok := err.(*tVerboseError) | |
if !ok { | |
return make([]StackEntry, 0) | |
} | |
return verbose_error.trace | |
} | |
func MakeError(format string, args ...interface{}) error { | |
return WrapError(errors.New(fmt.Sprintf(format, args...))) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment