diff --git a/go.mod b/go.mod index ed794278..99ad033c 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,11 @@ module github.com/rs/zerolog + +go 1.12 + +require ( + github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e + github.com/pkg/errors v0.8.1 + github.com/rs/xid v1.2.1 + github.com/zenazn/goji v0.9.0 + golang.org/x/tools v0.0.0-20190321154406-ae772f11d294 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..8ee518ad --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190321154406-ae772f11d294 h1:gMPF/7U3xs3RNmp7bh9BoJnRg81e5uKSYpyaGoAmadw= +golang.org/x/tools v0.0.0-20190321154406-ae772f11d294/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/pkgerrors/stacktrace.go b/pkgerrors/stacktrace.go index 01420e64..fe40eb50 100644 --- a/pkgerrors/stacktrace.go +++ b/pkgerrors/stacktrace.go @@ -40,13 +40,14 @@ func frameField(f errors.Frame, s *state, c rune) string { return string(s.b) } +type stackTracer interface { + StackTrace() errors.StackTrace +} + // 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 @@ -63,3 +64,60 @@ func MarshalStack(err error) interface{} { } return out } + +type stackTrace struct { + Frames []frame `json:"stacktrace"` +} + +type frame struct { + StackSourceFileName string `json:"source"` + StackSourceLineName string `json:"line"` + StackSourceFuncName string `json:"func"` +} + +// MarshalMultiStack properly implements pkg/errors stack trace marshaling by unwrapping the error stack. +// +// zerolog.ErrorStackMarshaler = MarshalMultiStack +func MarshalMultiStack(err error) interface{} { + stackTraces := []stackTrace{} + currentErr := err + for currentErr != nil { + stack, ok := currentErr.(stackTracer) + if !ok { + // Unwrap again because errors.Wrap actually adds two + // layers of wrapping. + currentErr = unwrapErr(currentErr) + continue + } + st := stack.StackTrace() + s := &state{} + stackTrace := stackTrace{} + for _, f := range st { + frame := frame{ + StackSourceFileName: frameField(f, s, 's'), + StackSourceLineName: frameField(f, s, 'd'), + StackSourceFuncName: frameField(f, s, 'n'), + } + stackTrace.Frames = append(stackTrace.Frames, frame) + } + stackTraces = append(stackTraces, stackTrace) + + currentErr = unwrapErr(currentErr) + } + return stackTraces +} + +type causer interface { + Cause() error +} + +// Some methods of wrapping cause more layers of wrapping than other layers, +// e.g. errors.New, errors.WithStack and errors.WithMessage add one layer of +// wrapping, whereas errors.Wrap adds two layers of wrapping. +func unwrapErr(err error) error { + cause, ok := err.(causer) + if !ok { + return nil + } + return cause.Cause() +} diff --git a/pkgerrors/stacktrace_test.go b/pkgerrors/stacktrace_test.go index e771317d..f6421208 100644 --- a/pkgerrors/stacktrace_test.go +++ b/pkgerrors/stacktrace_test.go @@ -27,6 +27,24 @@ func TestLogStack(t *testing.T) { } } +func TestLogMultiStack(t *testing.T) { + zerolog.ErrorStackMarshaler = MarshalMultiStack + + out := &bytes.Buffer{} + log := zerolog.New(out) + + err := errors.New("error message") + err = errors.Wrap(err, "from error") + log.Log().Stack().Err(err).Msg("") + + got := out.String() + want := `\{"stack":\[\{"stacktrace":\[\{"source":"stacktrace_test.go","line":"37","func":"TestLogMultiStack"\},.*\{"stacktrace":\[\{"source":"stacktrace_test.go","line":"36","func":"TestLogMultiStack"\}.*\],"error":"from error: error message"\}` + 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{}