Add support for stack trace extration of error fields
This commit is contained in:
parent
27e0a22cbc
commit
3786fbfa73
14
context.go
14
context.go
|
@ -316,3 +316,17 @@ func (c Context) Caller() Context {
|
||||||
c.l = c.l.Hook(ch)
|
c.l = c.l.Hook(ch)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stackTraceHook struct{}
|
||||||
|
|
||||||
|
func (sh stackTraceHook) Run(e *Event, level Level, msg string) {
|
||||||
|
e.Stack()
|
||||||
|
}
|
||||||
|
|
||||||
|
var sh = callerHook{}
|
||||||
|
|
||||||
|
// Stack enables stack trace printing for the error passed to Err().
|
||||||
|
func (c Context) Stack() Context {
|
||||||
|
c.l = c.l.Hook(sh)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
22
event.go
22
event.go
|
@ -27,6 +27,7 @@ type Event struct {
|
||||||
w LevelWriter
|
w LevelWriter
|
||||||
level Level
|
level Level
|
||||||
done func(msg string)
|
done func(msg string)
|
||||||
|
stack bool // enable error stack trace
|
||||||
ch []Hook // hooks from context
|
ch []Hook // hooks from context
|
||||||
h []Hook
|
h []Hook
|
||||||
}
|
}
|
||||||
|
@ -242,17 +243,38 @@ func (e *Event) Errs(key string, errs []error) *Event {
|
||||||
|
|
||||||
// Err adds the field "error" with err as a string to the *Event context.
|
// Err adds the field "error" with err as a string to the *Event context.
|
||||||
// If err is nil, no field is added.
|
// If err is nil, no field is added.
|
||||||
|
//
|
||||||
// To customize the key name, change zerolog.ErrorFieldName.
|
// To customize the key name, change zerolog.ErrorFieldName.
|
||||||
|
//
|
||||||
|
// If Stack() has been called before and zerolog.ErrorStackMarshaler is defined,
|
||||||
|
// the err is passed to ErrorStackMarshaler and the result is appended to the
|
||||||
|
// zerolog.ErrorStackFieldName.
|
||||||
func (e *Event) Err(err error) *Event {
|
func (e *Event) Err(err error) *Event {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
if e.stack && ErrorStackMarshaler != nil {
|
||||||
|
s := ErrorStackMarshaler(err)
|
||||||
|
if len(s) > 0 {
|
||||||
|
e.buf = append(json.AppendKey(e.buf, ErrorStackFieldName), s...)
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.buf = json.AppendError(json.AppendKey(e.buf, ErrorFieldName), err)
|
e.buf = json.AppendError(json.AppendKey(e.buf, ErrorFieldName), err)
|
||||||
}
|
}
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stack enables stack trace printing for the error passed to Err().
|
||||||
|
//
|
||||||
|
// ErrorStackMarshaler must be set for this method to do something.
|
||||||
|
func (e *Event) Stack() *Event {
|
||||||
|
if e != nil {
|
||||||
|
e.stack = true
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
// Bool adds the field key with val as a bool to the *Event context.
|
// Bool adds the field key with val as a bool to the *Event context.
|
||||||
func (e *Event) Bool(key string, b bool) *Event {
|
func (e *Event) Bool(key string, b bool) *Event {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
|
|
|
@ -19,6 +19,13 @@ var (
|
||||||
// CallerFieldName is the field name used for caller field.
|
// CallerFieldName is the field name used for caller field.
|
||||||
CallerFieldName = "caller"
|
CallerFieldName = "caller"
|
||||||
|
|
||||||
|
// ErrorStackFieldName is the field name used for error stacks.
|
||||||
|
ErrorStackFieldName = "stack"
|
||||||
|
|
||||||
|
// ErrorStackMarshaler extract the stack from err if any, and returns it as
|
||||||
|
// a marshaled JSON.
|
||||||
|
ErrorStackMarshaler func(err error) []byte
|
||||||
|
|
||||||
// TimeFieldFormat defines the time format of the Time field type.
|
// TimeFieldFormat defines the time format of the Time field type.
|
||||||
// If set to an empty string, the time is formatted as an UNIX timestamp
|
// If set to an empty string, the time is formatted as an UNIX timestamp
|
||||||
// as integer.
|
// as integer.
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package pkgerrors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/rs/zerolog/internal/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
StackSourceFileName = "source"
|
||||||
|
StackSourceLineName = "line"
|
||||||
|
StackSourceFunctionName = "func"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalStack implements pkg/errors stack trace marshaling.
|
||||||
|
//
|
||||||
|
// zerolog.ErrorStackMarshaler = MarshalStack
|
||||||
|
func MarshalStack(err error) []byte {
|
||||||
|
type stackTracer interface {
|
||||||
|
StackTrace() errors.StackTrace
|
||||||
|
}
|
||||||
|
var st errors.StackTrace
|
||||||
|
if err, ok := err.(stackTracer); ok {
|
||||||
|
st = err.StackTrace()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return appendJSONStack(make([]byte, 0, 500), st)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendJSONStack(dst []byte, st errors.StackTrace) []byte {
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 100))
|
||||||
|
dst = append(dst, '[')
|
||||||
|
for i, frame := range st {
|
||||||
|
if i > 0 {
|
||||||
|
dst = append(dst, ',')
|
||||||
|
}
|
||||||
|
|
||||||
|
dst = append(dst, '{')
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%s", frame)
|
||||||
|
dst = json.AppendString(dst, StackSourceFileName)
|
||||||
|
dst = append(dst, ':')
|
||||||
|
dst = json.AppendBytes(dst, buf.Bytes())
|
||||||
|
dst = append(dst, ',')
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%d", frame)
|
||||||
|
dst = json.AppendString(dst, StackSourceLineName)
|
||||||
|
dst = append(dst, ':')
|
||||||
|
dst = json.AppendBytes(dst, buf.Bytes())
|
||||||
|
dst = append(dst, ',')
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%n", frame)
|
||||||
|
dst = json.AppendString(dst, StackSourceFunctionName)
|
||||||
|
dst = append(dst, ':')
|
||||||
|
dst = json.AppendBytes(dst, buf.Bytes())
|
||||||
|
|
||||||
|
dst = append(dst, '}')
|
||||||
|
}
|
||||||
|
dst = append(dst, ']')
|
||||||
|
return dst
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package pkgerrors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogStack(t *testing.T) {
|
||||||
|
zerolog.ErrorStackMarshaler = MarshalStack
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
log := zerolog.New(out)
|
||||||
|
|
||||||
|
err := errors.Wrap(errors.New("error message"), "from error")
|
||||||
|
log.Log().Stack().Err(err).Msg("")
|
||||||
|
|
||||||
|
got := out.String()
|
||||||
|
want := `\{"stack":\[\{"source":"stacktrace_test.go","line":"18","func":"TestLogStack"\},.*\],"error":"from error: error message"\}\n`
|
||||||
|
if ok, _ := regexp.MatchString(want, got); !ok {
|
||||||
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue