Proposal: adding custom fields to errors
masterada opened this issue · 3 comments
masterada commented
I suggest a WithFields() method that lets you add arbitrary data to your error object, that can later be fetched (even if it was added to a nested error). We use this with alternating key-values for adding useful information to our errors for logging them later.
Usage:
err := errors.New("My error")
err = errors.WithFields(err, MyCustomKeyValue{ key: "statusCode", value: 200})
err = errors.WithFields(err, "any data", "can be added", 123)
err = errors.WithStack(err)
fields := errors.Fields(err)
Implementation:
type withFields struct {
cause error
fields []interface{}
}
func WithFields(err error, fields ...interface{}) error {
if len(fields) == 0 {
return err
}
return &withFields{
cause: err,
fields: fields,
}
}
func (e *withFields) Error() string {
return e.cause.Error()
}
func (e *withFields) Fields() []interface{} {
return e.fields
}
func (e *withFields) Cause() error {
return e.cause
}
func (e *withFields) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v\n", e.Cause())
return
}
fallthrough
case 's', 'q':
io.WriteString(s, e.Error())
}
}
func Fields(err error) []interface{} {
type causer interface {
Cause() error
}
type fielder interface {
Fields() []interface{}
}
var fields []interface{}
for err != nil {
if fErr, ok := err.(fielder); ok {
fields = append(fields, fErr.Fields()...)
}
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return fields
}
davecheney commented
Duplicate of #70
davecheney commented
Duplicate of #34
masterada commented
OK, sorry for duplicating, didn't check closed issues.