-
-
Save sokolovstas/7af1b7710bee85c77246e27acbe13796 to your computer and use it in GitHub Desktop.
Support for github.com/pkg/errors's stacktrace to github.com/getsentry/raven-go kudos to @pierrre
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package ravenerrors adds support for github.com/pkg/errors's stacktrace to github.com/getsentry/raven-go. | |
// | |
// https://github.com/getsentry/raven-go/issues/88#issuecomment-269002948 | |
// | |
// 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 ( | |
"reflect" | |
"regexp" | |
"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 client.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.NewExceptionWithClient. | |
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 ravenNewException(myerr, st) | |
} | |
// Modifies raven-go.NewException | |
var errorMsgPattern = regexp.MustCompile(`\A(\w+): (.+)\z`) | |
func ravenNewException(err error, stacktrace *raven.Stacktrace) *raven.Exception { | |
msg := err.Error() | |
ex := &raven.Exception{ | |
Stacktrace: stacktrace, | |
Value: msg, | |
Type: reflect.TypeOf(errors.Cause(err)).String(), | |
} | |
if m := errorMsgPattern.FindStringSubmatch(msg); m != nil { | |
ex.Module, ex.Value = m[1], m[2] | |
} | |
return ex | |
} | |
// 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) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment