Compare commits
5 Commits
master
...
stacktrace
Author | SHA1 | Date |
---|---|---|
Olivier Poitrey | 9f1f25d2fa | |
Olivier Poitrey | 6d2153805b | |
Olivier Poitrey | 2988c1e444 | |
Olivier Poitrey | adb19ff852 | |
Olivier Poitrey | 3786fbfa73 |
14
context.go
14
context.go
|
@ -362,6 +362,20 @@ func (c Context) Caller() Context {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stackTraceHook struct{}
|
||||||
|
|
||||||
|
func (sh stackTraceHook) Run(e *Event, level Level, msg string) {
|
||||||
|
e.Stack()
|
||||||
|
}
|
||||||
|
|
||||||
|
var sh = stackTraceHook{}
|
||||||
|
|
||||||
|
// Stack enables stack trace printing for the error passed to Err().
|
||||||
|
func (c Context) Stack() Context {
|
||||||
|
c.l = c.l.Hook(sh)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// IPAddr adds IPv4 or IPv6 Address to the context
|
// IPAddr adds IPv4 or IPv6 Address to the context
|
||||||
func (c Context) IPAddr(key string, ip net.IP) Context {
|
func (c Context) IPAddr(key string, ip net.IP) Context {
|
||||||
c.l.context = enc.AppendIPAddr(enc.AppendKey(c.l.context, key), ip)
|
c.l.context = enc.AppendIPAddr(enc.AppendKey(c.l.context, key), ip)
|
||||||
|
|
48
event.go
48
event.go
|
@ -18,11 +18,6 @@ var eventPool = &sync.Pool{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorMarshalFunc allows customization of global error marshaling
|
|
||||||
var ErrorMarshalFunc = func(err error) interface{} {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event represents a log event. It is instanced by one of the level method of
|
// Event represents a log event. It is instanced by one of the level method of
|
||||||
// Logger and finalized by the Msg or Msgf method.
|
// Logger and finalized by the Msg or Msgf method.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
|
@ -30,6 +25,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,8 +267,10 @@ func (e *Event) RawJSON(key string, b []byte) *Event {
|
||||||
// AnErr adds the field key with serialized err to the *Event context.
|
// AnErr adds the field key with serialized err to the *Event context.
|
||||||
// If err is nil, no field is added.
|
// If err is nil, no field is added.
|
||||||
func (e *Event) AnErr(key string, err error) *Event {
|
func (e *Event) AnErr(key string, err error) *Event {
|
||||||
marshaled := ErrorMarshalFunc(err)
|
if e == nil {
|
||||||
switch m := marshaled.(type) {
|
return e
|
||||||
|
}
|
||||||
|
switch m := ErrorMarshalFunc(err).(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return e
|
return e
|
||||||
case LogObjectMarshaler:
|
case LogObjectMarshaler:
|
||||||
|
@ -292,11 +290,9 @@ func (e *Event) Errs(key string, errs []error) *Event {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
arr := Arr()
|
arr := Arr()
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
marshaled := ErrorMarshalFunc(err)
|
switch m := ErrorMarshalFunc(err).(type) {
|
||||||
switch m := marshaled.(type) {
|
|
||||||
case LogObjectMarshaler:
|
case LogObjectMarshaler:
|
||||||
arr = arr.Object(m)
|
arr = arr.Object(m)
|
||||||
case error:
|
case error:
|
||||||
|
@ -314,10 +310,42 @@ func (e *Event) Errs(key string, errs []error) *Event {
|
||||||
// Err adds the field "error" with serialized err to the *Event context.
|
// Err adds the field "error" with serialized err 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.
|
||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e.stack && ErrorStackMarshaler != nil {
|
||||||
|
switch m := ErrorStackMarshaler(err).(type) {
|
||||||
|
case nil:
|
||||||
|
case LogObjectMarshaler:
|
||||||
|
e.Object(ErrorStackFieldName, m)
|
||||||
|
case error:
|
||||||
|
e.Str(ErrorStackFieldName, m.Error())
|
||||||
|
case string:
|
||||||
|
e.Str(ErrorStackFieldName, m)
|
||||||
|
default:
|
||||||
|
e.Interface(ErrorStackFieldName, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
return e.AnErr(ErrorFieldName, err)
|
return e.AnErr(ErrorFieldName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
|
11
globals.go
11
globals.go
|
@ -22,6 +22,17 @@ var (
|
||||||
// CallerSkipFrameCount is the number of stack frames to skip to find the caller.
|
// CallerSkipFrameCount is the number of stack frames to skip to find the caller.
|
||||||
CallerSkipFrameCount = 2
|
CallerSkipFrameCount = 2
|
||||||
|
|
||||||
|
// ErrorStackFieldName is the field name used for error stacks.
|
||||||
|
ErrorStackFieldName = "stack"
|
||||||
|
|
||||||
|
// ErrorStackMarshaler extract the stack from err if any.
|
||||||
|
ErrorStackMarshaler func(err error) interface{}
|
||||||
|
|
||||||
|
// ErrorMarshalFunc allows customization of global error marshaling
|
||||||
|
ErrorMarshalFunc = func(err error) interface{} {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 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,65 @@
|
||||||
|
package pkgerrors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
StackSourceFileName = "source"
|
||||||
|
StackSourceLineName = "line"
|
||||||
|
StackSourceFunctionName = "func"
|
||||||
|
)
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
b []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implement fmt.Formatter interface.
|
||||||
|
func (s *state) Write(b []byte) (n int, err error) {
|
||||||
|
s.b = b
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width implement fmt.Formatter interface.
|
||||||
|
func (s *state) Width() (wid int, ok bool) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precision implement fmt.Formatter interface.
|
||||||
|
func (s *state) Precision() (prec int, ok bool) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag implement fmt.Formatter interface.
|
||||||
|
func (s *state) Flag(c int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func frameField(f errors.Frame, s *state, c rune) string {
|
||||||
|
f.Format(s, c)
|
||||||
|
return string(s.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalStack implements pkg/errors stack trace marshaling.
|
||||||
|
//
|
||||||
|
// zerolog.ErrorStackMarshaler = MarshalStack
|
||||||
|
func MarshalStack(err error) interface{} {
|
||||||
|
type stackTracer interface {
|
||||||
|
StackTrace() errors.StackTrace
|
||||||
|
}
|
||||||
|
sterr, ok := err.(stackTracer)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
st := sterr.StackTrace()
|
||||||
|
s := &state{}
|
||||||
|
out := make([]map[string]string, 0, len(st))
|
||||||
|
for _, frame := range st {
|
||||||
|
out = append(out, map[string]string{
|
||||||
|
StackSourceFileName: frameField(frame, s, 's'),
|
||||||
|
StackSourceLineName: frameField(frame, s, 'd'),
|
||||||
|
StackSourceFunctionName: frameField(frame, s, 'n'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// +build !binary_log
|
||||||
|
|
||||||
|
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":\[\{"func":"TestLogStack","line":"20","source":"stacktrace_test.go"\},.*\],"error":"from error: error message"\}\n`
|
||||||
|
if ok, _ := regexp.MatchString(want, got); !ok {
|
||||||
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLogStack(b *testing.B) {
|
||||||
|
zerolog.ErrorStackMarshaler = MarshalStack
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
log := zerolog.New(out)
|
||||||
|
err := errors.Wrap(errors.New("error message"), "from error")
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
log.Log().Stack().Err(err).Msg("")
|
||||||
|
out.Reset()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue