forked from Silverfish/proton-bridge
Update sentry client
This commit is contained in:
@ -1,6 +1,10 @@
|
|||||||
# ProtonMail Bridge and Import-Export app Changelog
|
# ProtonMail Bridge and Import-Export app Changelog
|
||||||
|
|
||||||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
## Untaged
|
||||||
|
### Changed
|
||||||
|
* GODT-180 Updated Sentry client.
|
||||||
|
|
||||||
|
|
||||||
## [IE 1.2.1] Elbe
|
## [IE 1.2.1] Elbe
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/constants"
|
"github.com/ProtonMail/proton-bridge/pkg/constants"
|
||||||
"github.com/getsentry/raven-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -51,10 +51,14 @@ var (
|
|||||||
|
|
||||||
// Main sets up Sentry, filters out unwanted args, creates app and runs it.
|
// Main sets up Sentry, filters out unwanted args, creates app and runs it.
|
||||||
func Main(appName, usage string, extraFlags []cli.Flag, run func(*cli.Context) error) {
|
func Main(appName, usage string, extraFlags []cli.Flag, run func(*cli.Context) error) {
|
||||||
if err := raven.SetDSN(constants.DSNSentry); err != nil {
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
|
Dsn: constants.DSNSentry,
|
||||||
|
Release: constants.Revision,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
log.WithError(err).Errorln("Can not setup sentry DSN")
|
log.WithError(err).Errorln("Can not setup sentry DSN")
|
||||||
}
|
}
|
||||||
raven.SetRelease(constants.Revision)
|
|
||||||
|
|
||||||
filterProcessSerialNumberFromArgs()
|
filterProcessSerialNumberFromArgs()
|
||||||
filterRestartNumberFromArgs()
|
filterRestartNumberFromArgs()
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
package pmapi
|
package pmapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/getsentry/raven-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -119,7 +119,11 @@ func (c *client) UpdateUser() (user *User, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.user = user
|
c.user = user
|
||||||
raven.SetUserContext(&raven.User{ID: user.ID})
|
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||||
|
scope.SetUser(sentry.User{
|
||||||
|
ID: user.ID,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
var tmpList AddressList
|
var tmpList AddressList
|
||||||
if tmpList, err = c.GetAddresses(); err == nil {
|
if tmpList, err = c.GetAddresses(); err == nil {
|
||||||
|
|||||||
@ -18,135 +18,15 @@
|
|||||||
package sentry
|
package sentry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"time"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/getsentry/raven-go"
|
"github.com/getsentry/sentry-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const fileParseError = "[file parse error]"
|
// ReportSentryCrash reports a sentry crash.
|
||||||
|
|
||||||
var isGoroutine = regexp.MustCompile("^goroutine [[:digit:]]+.*") //nolint[gochecknoglobals]
|
|
||||||
|
|
||||||
// Threads implements standard sentry thread report.
|
|
||||||
type Threads struct {
|
|
||||||
Values []Thread `json:"values"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class specifier.
|
|
||||||
func (s *Threads) Class() string { return "threads" }
|
|
||||||
|
|
||||||
// Thread wraps a single stacktrace.
|
|
||||||
type Thread struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Crashed bool `json:"crashed"`
|
|
||||||
Stacktrace *raven.Stacktrace `json:"stacktrace"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TraceAllRoutines traces all goroutines and saves them to the current object.
|
|
||||||
func (s *Threads) TraceAllRoutines() {
|
|
||||||
s.Values = []Thread{}
|
|
||||||
goroutines := &strings.Builder{}
|
|
||||||
_ = pprof.Lookup("goroutine").WriteTo(goroutines, 2)
|
|
||||||
|
|
||||||
thread := Thread{ID: -1}
|
|
||||||
var frame *raven.StacktraceFrame
|
|
||||||
for _, v := range strings.Split(goroutines.String(), "\n") {
|
|
||||||
// Ignore empty lines.
|
|
||||||
if v == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// New routine.
|
|
||||||
if isGoroutine.MatchString(v) {
|
|
||||||
if thread.ID >= 0 {
|
|
||||||
s.Values = append(s.Values, thread)
|
|
||||||
}
|
|
||||||
thread = Thread{ID: thread.ID + 1, Name: v, Crashed: thread.ID == -1, Stacktrace: &raven.Stacktrace{Frames: []*raven.StacktraceFrame{}}}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// New function.
|
|
||||||
if frame == nil {
|
|
||||||
frame = &raven.StacktraceFrame{Function: v}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set filename and add frame.
|
|
||||||
if frame.Filename == "" {
|
|
||||||
fld := strings.Fields(v)
|
|
||||||
if len(fld) != 2 {
|
|
||||||
frame.Filename = fileParseError
|
|
||||||
frame.AbsolutePath = v
|
|
||||||
} else {
|
|
||||||
frame.Filename = fld[0]
|
|
||||||
sp := strings.Split(fld[0], ":")
|
|
||||||
if len(sp) > 1 {
|
|
||||||
i, err := strconv.Atoi(sp[len(sp)-1])
|
|
||||||
if err == nil {
|
|
||||||
frame.Filename = strings.Join(sp[:len(sp)-1], ":")
|
|
||||||
frame.Lineno = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if frame.AbsolutePath == "" && frame.Filename != fileParseError {
|
|
||||||
frame.AbsolutePath = frame.Filename
|
|
||||||
if sp := strings.Split(frame.Filename, "/"); len(sp) > 1 {
|
|
||||||
frame.Filename = sp[len(sp)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
thread.Stacktrace.Frames = append([]*raven.StacktraceFrame{frame}, thread.Stacktrace.Frames...)
|
|
||||||
frame = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add last thread.
|
|
||||||
s.Values = append(s.Values, thread)
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPanicSender(s *Threads, err error) string {
|
|
||||||
out := "error nil"
|
|
||||||
if err != nil {
|
|
||||||
out = err.Error()
|
|
||||||
}
|
|
||||||
for _, thread := range s.Values {
|
|
||||||
if !thread.Crashed {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for i, fr := range thread.Stacktrace.Frames {
|
|
||||||
if strings.HasSuffix(fr.Filename, "panic.go") && strings.HasPrefix(fr.Function, "panic") {
|
|
||||||
// Next frame if any.
|
|
||||||
j := 0
|
|
||||||
if i > j {
|
|
||||||
j = i - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Directory and filename.
|
|
||||||
fname := thread.Stacktrace.Frames[j].AbsolutePath
|
|
||||||
if sp := strings.Split(fname, "/"); len(sp) > 2 {
|
|
||||||
fname = strings.Join(sp[len(sp)-2:], "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Line number.
|
|
||||||
if ln := thread.Stacktrace.Frames[j].Lineno; ln > 0 {
|
|
||||||
fname = fmt.Sprintf("%s:%d", fname, ln)
|
|
||||||
}
|
|
||||||
|
|
||||||
out = fmt.Sprintf("%s: %s", fname, out)
|
|
||||||
break // Just first panic.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportSentryCrash reports a sentry crash with stacktrace from all goroutines.
|
|
||||||
func ReportSentryCrash(clientID, appVersion, userAgent string, reportErr error) (err error) {
|
func ReportSentryCrash(clientID, appVersion, userAgent string, reportErr error) (err error) {
|
||||||
if reportErr == nil {
|
if reportErr == nil {
|
||||||
return
|
return
|
||||||
@ -160,18 +40,16 @@ func ReportSentryCrash(clientID, appVersion, userAgent string, reportErr error)
|
|||||||
"UserID": "",
|
"UserID": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
threads := &Threads{}
|
sentry.WithScope(func(scope *sentry.Scope) {
|
||||||
threads.TraceAllRoutines()
|
scope.SetTags(tags)
|
||||||
errorWithFile := findPanicSender(threads, reportErr)
|
sentry.CaptureException(reportErr)
|
||||||
packet := raven.NewPacket(errorWithFile, threads)
|
})
|
||||||
|
|
||||||
eventID, ch := raven.Capture(packet, tags)
|
if !sentry.Flush(time.Second * 10) {
|
||||||
|
log.WithField("error", reportErr).Error("failed to report sentry error")
|
||||||
if err = <-ch; err == nil {
|
return errors.New("failed to report sentry error")
|
||||||
log.WithField("errorID", eventID).Warn("Reported sentry error")
|
|
||||||
} else {
|
|
||||||
log.WithField("error", reportErr).WithError(err).Error("Failed to report sentry error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
log.WithField("error", reportErr).Warn("reported sentry error")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package sentry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/getsentry/raven-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSentryCrashReport(t *testing.T) {
|
|
||||||
if err := ReportSentryCrash(
|
|
||||||
"clientID",
|
|
||||||
"appVersion",
|
|
||||||
"useragent",
|
|
||||||
errors.New("Testing crash report - api proxy; goroutines with threads, find origin"),
|
|
||||||
); err != nil {
|
|
||||||
t.Fatal("Expected no error while report, but have", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Threads) TraceAllRoutinesTest() {
|
|
||||||
s.Values = []Thread{
|
|
||||||
{
|
|
||||||
ID: 0,
|
|
||||||
Name: "goroutine 20 [running]",
|
|
||||||
Crashed: true,
|
|
||||||
Stacktrace: &raven.Stacktrace{
|
|
||||||
Frames: []*raven.StacktraceFrame{
|
|
||||||
{
|
|
||||||
Filename: "/home/dev/build/go-1.10.2/go/src/runtime/pprof/pprof.go",
|
|
||||||
Function: "runtime/pprof.writeGoroutineStacks(0x9b7de0, 0xc4203e2900, 0xd0, 0xd0)",
|
|
||||||
Lineno: 650,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Name: "goroutine 20 [chan receive]",
|
|
||||||
Crashed: false,
|
|
||||||
Stacktrace: &raven.Stacktrace{
|
|
||||||
Frames: []*raven.StacktraceFrame{
|
|
||||||
{
|
|
||||||
Filename: "/home/dev/build/go-1.10.2/go/src/testing/testing.go",
|
|
||||||
Function: "testing.(*T).Run(0xc4203e42d0, 0x90f445, 0x15, 0x97d358, 0x47a501)",
|
|
||||||
Lineno: 825,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user