Skip to content

Instantly share code, notes, and snippets.

@sokolovstas
Forked from choonkeat/getsentry.go
Created April 28, 2017 20:09
Show Gist options
  • Save sokolovstas/7af1b7710bee85c77246e27acbe13796 to your computer and use it in GitHub Desktop.
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
// 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