-
-
Notifications
You must be signed in to change notification settings - Fork 148
Do you support github.com/pkg/errors stacktrace ? #88
Comments
Hello! Here is the code I plan to use. If we want to integrate it to
By the way, I also fixed:
// Package ravenerrors adds support for github.com/pkg/errors's stacktrace to github.com/getsentry/raven-go.
//
// It works as a replacement for github.com/getsentry/raven-go.CaptureError*.
// Stacktraces are extracted from the error if available and replace raven's default behavior.
package ravenerrors
import (
"runtime"
"github.com/getsentry/raven-go"
"github.com/pkg/errors"
)
// Capture is a replacement for github.com/getsentry/raven-go.CaptureError.
func Capture(myerr error, tags map[string]string, interfaces ...raven.Interface) (string, <-chan error) {
return capture(raven.DefaultClient, myerr, tags, interfaces...)
}
// CaptureWithClient is a replacement for github.com/getsentry/raven-go.Client.CaptureError.
func CaptureWithClient(client *raven.Client, myerr error, tags map[string]string, interfaces ...raven.Interface) (string, <-chan error) {
return capture(client, myerr, tags, interfaces...)
}
// CaptureAndWait is a replacement for github.com/getsentry/raven-go.CaptureErrorAndWait.
func CaptureAndWait(myerr error, tags map[string]string, interfaces ...raven.Interface) (string, error) {
eventID, errch := capture(raven.DefaultClient, myerr, tags, interfaces...)
err := <-errch
return eventID, err
}
// CaptureAndWaitWithClient is a replacement for github.com/getsentry/raven-go.Client.CaptureErrorAndWait.
func CaptureAndWaitWithClient(client *raven.Client, myerr error, tags map[string]string, interfaces ...raven.Interface) (string, error) {
eventID, errch := capture(client, myerr, tags, interfaces...)
err := <-errch
return eventID, err
}
func capture(client *raven.Client, myerr error, tags map[string]string, interfaces ...raven.Interface) (string, <-chan error) {
st := getErrorStackTraceConverted(myerr, 3, client.IncludePaths())
if st == nil {
st = raven.NewStacktrace(2, 3, client.IncludePaths())
}
ex := raven.NewException(myerr, st)
packet := raven.NewPacket(myerr.Error(), append(interfaces, ex)...)
return client.Capture(packet, tags)
}
func getErrorStackTraceConverted(err error, context int, appPackagePrefixes []string) *raven.Stacktrace {
st := getErrorCauseStackTrace(err)
if st == nil {
return nil
}
return convertStackTrace(st, context, appPackagePrefixes)
}
func getErrorCauseStackTrace(err error) errors.StackTrace {
// This code is inspired by github.com/pkg/errors.Cause().
var st errors.StackTrace
for err != nil {
s := getErrorStackTrace(err)
if s != nil {
st = s
}
err = getErrorCause(err)
}
return st
}
func getErrorStackTrace(err error) errors.StackTrace {
ster, ok := err.(interface {
StackTrace() errors.StackTrace
})
if !ok {
return nil
}
return ster.StackTrace()
}
func getErrorCause(err error) error {
cer, ok := err.(interface {
Cause() error
})
if !ok {
return nil
}
return cer.Cause()
}
func convertStackTrace(st errors.StackTrace, context int, appPackagePrefixes []string) *raven.Stacktrace {
// This code is borrowed from github.com/getsentry/raven-go.NewStacktrace().
var frames []*raven.StacktraceFrame
for _, f := range st {
frame := convertFrame(f, context, appPackagePrefixes)
if frame != nil {
frames = append(frames, frame)
}
}
if len(frames) == 0 {
return nil
}
for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
frames[i], frames[j] = frames[j], frames[i]
}
return &raven.Stacktrace{Frames: frames}
}
func convertFrame(f errors.Frame, context int, appPackagePrefixes []string) *raven.StacktraceFrame {
// This code is borrowed from github.com/pkg/errors.Frame.
pc := uintptr(f) - 1
fn := runtime.FuncForPC(pc)
var file string
var line int
if fn != nil {
file, line = fn.FileLine(pc)
} else {
file = "unknown"
}
return raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes)
} |
@pierrre is definitely right on this issue, if we don't have a good stacktrace on Sentry, it's completely useless to use it or to pay for it since you can't investigate properly with the stacktrace shown on Sentry. @mattrobenolt are you planning to support |
FYI, the latest version of my wrapper: // Package ravenerrors adds support for github.com/pkg/errors's stacktrace to github.com/getsentry/raven-go.
//
// It works as a replacement for github.com/getsentry/raven-go.CaptureError*.
// Stacktraces are extracted from the error if available and replace raven's default behavior.
package ravenerrors
import (
"runtime"
"github.com/getsentry/raven-go"
"github.com/pkg/errors"
)
// CaptureAndWait is a replacement for github.com/getsentry/raven-go.CaptureErrorAndWait.
func CaptureAndWait(myerr error, tags map[string]string, interfaces ...raven.Interface) (string, error) {
return captureAndWait(raven.DefaultClient, myerr, 1, tags, interfaces...)
}
// CaptureAndWaitWithClient is a replacement for github.com/getsentry/raven-go.Client.CaptureErrorAndWait.
func CaptureAndWaitWithClient(client *raven.Client, myerr error, tags map[string]string, interfaces ...raven.Interface) (string, error) {
return captureAndWait(client, myerr, 1, tags, interfaces...)
}
func captureAndWait(client *raven.Client, myerr error, skip int, tags map[string]string, interfaces ...raven.Interface) (string, error) {
eventID, errch := capture(client, myerr, skip+1, tags, interfaces...)
err := <-errch
return eventID, err
}
// Capture is a replacement for github.com/getsentry/raven-go.CaptureError.
func Capture(myerr error, tags map[string]string, interfaces ...raven.Interface) (string, <-chan error) {
return capture(raven.DefaultClient, myerr, 1, tags, interfaces...)
}
// CaptureWithClient is a replacement for github.com/getsentry/raven-go.Client.CaptureError.
func CaptureWithClient(client *raven.Client, myerr error, tags map[string]string, interfaces ...raven.Interface) (string, <-chan error) {
return capture(client, myerr, 1, tags, interfaces...)
}
func capture(client *raven.Client, myerr error, skip int, tags map[string]string, interfaces ...raven.Interface) (string, <-chan error) {
p := newPacket(client, myerr, skip+1, interfaces...)
return raven.Capture(p, tags)
}
// NewPacket is a replacement for github.com/getsentry/raven-go.Client.NewPacket.
func NewPacket(myerr error, interfaces ...raven.Interface) *raven.Packet {
return newPacket(raven.DefaultClient, myerr, 1, interfaces...)
}
// NewPacketWithClient is a replacement for github.com/getsentry/raven-go.NewPacket.
func NewPacketWithClient(client *raven.Client, myerr error, interfaces ...raven.Interface) *raven.Packet {
return newPacket(client, myerr, 1, interfaces...)
}
func newPacket(client *raven.Client, myerr error, skip int, interfaces ...raven.Interface) *raven.Packet {
ex := newException(client, myerr, skip+1)
return raven.NewPacket(myerr.Error(), append(interfaces, ex)...)
}
// NewException is a replacement for github.com/getsentry/raven-go.NewException.
func NewException(myerr error) *raven.Exception {
return newException(raven.DefaultClient, myerr, 1)
}
// NewExceptionWithClient is a replacement for github.com/getsentry/raven-go.NewException.
func NewExceptionWithClient(client *raven.Client, myerr error) *raven.Exception {
return newException(client, myerr, 1)
}
func newException(client *raven.Client, myerr error, skip int) *raven.Exception {
st := newStackTrace(client, myerr, skip+1)
return raven.NewException(myerr, st)
}
// NewStackTrace is a replacement for github.com/getsentry/raven-go.NewStackTrace.
func NewStackTrace(myerr error) *raven.Stacktrace {
return newStackTrace(raven.DefaultClient, myerr, 1)
}
// NewStackTraceWithClient is a replacement for github.com/getsentry/raven-go.NewStackTrace.
func NewStackTraceWithClient(client *raven.Client, myerr error) *raven.Stacktrace {
return newStackTrace(client, myerr, 1)
}
func newStackTrace(client *raven.Client, myerr error, skip int) *raven.Stacktrace {
st := getErrorStackTraceConverted(myerr, 3, client.IncludePaths())
if st == nil {
st = raven.NewStacktrace(skip+1, 3, client.IncludePaths())
}
return st
}
func getErrorStackTraceConverted(err error, context int, appPackagePrefixes []string) *raven.Stacktrace {
st := getErrorCauseStackTrace(err)
if st == nil {
return nil
}
return convertStackTrace(st, context, appPackagePrefixes)
}
func getErrorCauseStackTrace(err error) errors.StackTrace {
// This code is inspired by github.com/pkg/errors.Cause().
var st errors.StackTrace
for err != nil {
s := getErrorStackTrace(err)
if s != nil {
st = s
}
err = getErrorCause(err)
}
return st
}
func getErrorStackTrace(err error) errors.StackTrace {
ster, ok := err.(interface {
StackTrace() errors.StackTrace
})
if !ok {
return nil
}
return ster.StackTrace()
}
func getErrorCause(err error) error {
cer, ok := err.(interface {
Cause() error
})
if !ok {
return nil
}
return cer.Cause()
}
func convertStackTrace(st errors.StackTrace, context int, appPackagePrefixes []string) *raven.Stacktrace {
// This code is borrowed from github.com/getsentry/raven-go.NewStacktrace().
var frames []*raven.StacktraceFrame
for _, f := range st {
frame := convertFrame(f, context, appPackagePrefixes)
if frame != nil {
frames = append(frames, frame)
}
}
if len(frames) == 0 {
return nil
}
for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
frames[i], frames[j] = frames[j], frames[i]
}
return &raven.Stacktrace{Frames: frames}
}
func convertFrame(f errors.Frame, context int, appPackagePrefixes []string) *raven.StacktraceFrame {
// This code is borrowed from github.com/pkg/errors.Frame.
pc := uintptr(f) - 1
fn := runtime.FuncForPC(pc)
var file string
var line int
if fn != nil {
file, line = fn.FileLine(pc)
} else {
file = "unknown"
}
return raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes)
} |
Super thanks @pierrre I've updated your code to provide root error to Sentry instead. This allows the issue title to reflect the correct error type, e.g. https://gist.github.com/choonkeat/f6d45c31ff4e9f84cdfb9c1c0d54f1de +// https://github.com/getsentry/raven-go/issues/88#issuecomment-269002948
+//
// Package ravenerrors adds support for github.com/pkg/errors's stacktrace to github.com/getsentry/raven-go.
//
// It works as a replacement for github.com/getsentry/raven-go.CaptureError*.
@@ -69,7 +71,7 @@ func NewExceptionWithClient(client *raven.Client, myerr error) *raven.Exception
func newException(client *raven.Client, myerr error, skip int) *raven.Exception {
st := newStackTrace(client, myerr, skip+1)
- return raven.NewException(myerr, st)
+ return raven.NewException(errors.Cause(myerr), st)
}
// NewStackTrace is a replacement for github.com/getsentry/raven-go.NewStackTrace. |
@choonkeat thank you ! :) BTW, I just saw this https://docs.google.com/presentation/d/1Wcblp3jpfeKwA0Y4FOmj63PW52M_qmNqlQkNaLj0P5o/edit#slide=id.g1b2157b5d1_3_229 |
@choonkeat actually, I disagree func NewException(err error, stacktrace *Stacktrace) *Exception {
msg := err.Error()
ex := &Exception{
Stacktrace: stacktrace,
Value: msg,
Type: reflect.TypeOf(err).String(),
}
if m := errorMsgPattern.FindStringSubmatch(msg); m != nil {
ex.Module, ex.Value = m[1], m[2]
}
return ex
} If you call Edit: Maybe I'm wrong, because Edit2: OK, with my current code, the packet and exception messages are the same. |
@pierrre My bad, didn't notice the "message". I've modified the gist to perform |
+1 this would be really useful |
Note that there is pkg/errors#113 open that would help getting access to the stack frames! |
We're using CapturePanicAndWait and CaptureErrorAndWait so we couldn't use the gist or the fork mentioned above. We worked around it just now by getting the stack trace and sending it via CaptureMessage - not ideal but works for us until Sentry supports github.com/pkg/errors
|
Go SDK is now "error-package-agnostic" due to 238ebd8 |
Im using the master branch but my stack traces are still not good. My stack traces only include the line of the code that is calling capture error. :( e.g. func (e *ErrorSentry) Report(entry errorreporting.Entry) {
select {
case <-e.ctx.Done():
_ = e.Close()
return
default:
if entry.Req != nil {
e.c.SetHttpContext(raven.NewHttp(entry.Req))
}
if entry.User != "" {
e.c.SetUserContext(&raven.User{ID: entry.User})
}
e.c.CaptureError(entry.Error, nil)
e.c.ClearContext()
}
} my stack trace is always
eg of code calling this method ctxEnc, err := base64.StdEncoding.DecodeString(user.Context)
if err != nil {
errEntry.Error = errors.Wrap(err, "could not decode user context")
a.ErrReporter.Report(errEntry)
return nil
} This is really annoying as almost all my events are merged into 1 even tho none of the errors/events have nothing to do with each other only that the stack trace is the same. Does this have to do with the fact that im using a dockerized go binary without the source code? |
I don't understand how the current code can work. type stackTracer interface {
StackTrace() []runtime.Frame
} I don't see anything like that in |
@pierrre does #88 (comment) still work ? If it does than I would have to look into using it. Its a shame that the actual SDK doesn't work :( |
This code should still work, but I've updated it to use the new |
@pierrre Thanks! Your wrapper is providing me with stack traces! Thanks a lot :) |
In go, errors don't have a stacktrace.
Errors reported with Sentry/raven-go show the stacktrace of the
Capture
call, instead of where the errors come from.The
github.com/pkg/errors
package adds stack trace to Go errors. (you must wrap all errors witherrors.Wrap
)I don't think you currently support the stacktrace format of this package.
Do you plan to support it?
The text was updated successfully, but these errors were encountered: