Skip to content

Instantly share code, notes, and snippets.

@arnehormann
Last active August 29, 2015 13:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arnehormann/9971168 to your computer and use it in GitHub Desktop.
Save arnehormann/9971168 to your computer and use it in GitHub Desktop.
package mysql
import (
"errors"
"fmt"
"runtime"
"time"
)
var trace = false
type traceErr struct {
File string
Line int
Time time.Time
Err error
}
func newError(err error, stackdepth int) *traceErr {
now := time.Now()
_, file, line, _ := runtime.Caller(stackdepth)
// shorten filename like "log" does
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
return &traceErr{
File: short,
Line: line,
Time: now,
Err: err,
}
}
func Error(err error) error {
if err == nil || !trace {
return err
}
if _, isTraced := err.(*traceErr); isTraced {
return err
}
return newError(err, 2)
}
func ErrString(str string) error {
err := errors.New(str)
if trace {
return newError(err, 2)
}
return err
}
func ErrStringf(format string, args ...interface{}) error {
err := fmt.Errorf(format, args...)
if trace {
return newError(err, 2)
}
return err
}
func (err *traceErr) Error() string {
return err.Err.Error()
}
func (err *traceErr) String() string {
return fmt.Sprintf(
"%s %s:%d - %s",
err.Time.Format(time.RFC3339),
err.File,
err.Line,
err.Error(),
)
}
@arnehormann
Copy link
Author

A regular error as is. When setting trace = true, the errors can be used as a string:

if str, ok := err.(fmt.Stringer); ok {
    // Print it or do what you want - just make sure the printing function
    // doesn't try to convert it to error first - call String() to prevent that.
    fmt.Println(str.String())
}

The string will look like
2006-01-02T15:04:05Z07:00 source.go:13 - error message
and will include the filename and line number from wherever Error, ErrString or ErrStringf were originally called.

It's like log, but wraps errors directly.
If errors are returned anyway, wrapping them inside the nil-check-if

if err != nil {
    return Error(err)
}

should be negligable performance wise.
But the error can be serialized in a pretty way and provides a lot more information if it's passed around to another subsystem or even over the network. For intensive use, it should be exported. I just didn't want to pollute the namespace in my scenario.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment