@@ -2,9 +2,10 @@ package sentry
22
33import (
44 "fmt"
5+ "strings"
6+ "time"
57
6- "github.com/getsentry/raven-go"
7- "github.com/scaleway/scaleway-cli/v2/internal/core"
8+ "github.com/getsentry/sentry-go"
89 "github.com/scaleway/scaleway-sdk-go/logger"
910)
1011
@@ -17,44 +18,119 @@ An error occurred, we are sorry, please consider opening a ticket on github usin
1718Give us as many details as possible so we can reproduce the error and fix it.
1819---------------------------------------------------------------------------------------`
1920
20- // RecoverPanicAndSendReport will recover error if any, log them, and send them to sentry .
21- // It must be called with the defer built-in.
22- func RecoverPanicAndSendReport (buildInfo * core. BuildInfo , e interface {}) {
23- sentryClient , err := newSentryClient ( buildInfo )
21+ // RecoverPanicAndSendReport is to be called after recover .
22+ // It expects tags returned by core.BuildInfo and the recovered error
23+ func RecoverPanicAndSendReport (tags map [ string ] string , version string , e interface {}) {
24+ sentryHub , err := sentryHub ( tags , version )
2425 if err != nil {
25- logger .Debugf ("cannot create sentry client : %s" , err )
26+ logger .Debugf ("cannot get sentry hub : %s" , err )
2627 }
2728
2829 err , isError := e .(error )
2930 if isError {
30- logAndSentry (sentryClient , err )
31+ logAndSentry (sentryHub , err )
3132 } else {
32- logAndSentry (sentryClient , fmt .Errorf ("unknownw error: %v" , e ))
33+ logAndSentry (sentryHub , fmt .Errorf ("unknown error: %v" , e ))
3334 }
3435}
3536
36- func logAndSentry (sentryClient * raven. Client , err error ) {
37+ func logAndSentry (sentryHub * sentry. Hub , err error ) {
3738 logger .Errorf ("%s" , err )
38- if sentryClient != nil {
39- logger .Debugf ("sending sentry report: %s" , sentryClient .CaptureErrorAndWait (err , nil ))
39+ if sentryHub != nil {
40+ event := sentryHub .Recover (err )
41+ if event == nil {
42+ logger .Debugf ("failed to capture exception with sentry" )
43+ return
44+ }
45+ logger .Debugf ("sending sentry report: %s" , * event )
46+ if ! sentry .Flush (time .Second * 2 ) {
47+ logger .Debugf ("failed to send report" )
48+ }
4049 }
4150}
4251
4352// newSentryClient create a sentry client with build info tags.
44- func newSentryClient (buildInfo * core.BuildInfo ) (* raven.Client , error ) {
45- client , err := raven .New (dsn )
53+ func newSentryClient (version string ) (* sentry.Client , error ) {
54+ client , err := sentry .NewClient (sentry.ClientOptions {
55+ Dsn : dsn ,
56+ AttachStacktrace : true ,
57+ BeforeSend : func (event * sentry.Event , hint * sentry.EventHint ) * sentry.Event {
58+ filterStackFrames (event )
59+ return event
60+ },
61+ Release : version ,
62+ })
4663 if err != nil {
4764 return nil , err
4865 }
4966
50- tagsContext := map [string ]string {
51- "version" : buildInfo .Version .String (),
52- "go_arch" : buildInfo .GoArch ,
53- "go_os" : buildInfo .GoOS ,
54- "go_version" : buildInfo .GoVersion ,
67+ return client , nil
68+ }
69+
70+ func configureSentryScope (tags map [string ]string ) {
71+ sentry .ConfigureScope (func (scope * sentry.Scope ) {
72+ scope .SetTags (tags )
73+ })
74+ }
75+
76+ // AddCommandContext is used to pass executed command
77+ func AddCommandContext (line string ) {
78+ sentry .ConfigureScope (func (scope * sentry.Scope ) {
79+ scope .SetContext ("command" , map [string ]interface {}{
80+ "line" : line ,
81+ })
82+ })
83+ }
84+
85+ func AddArgumentsContext (args [][2 ]string ) {
86+ sentry .ConfigureScope (func (scope * sentry.Scope ) {
87+ argMap := map [string ]interface {}{}
88+
89+ for _ , arg := range args {
90+ argMap [arg [0 ]] = len (arg [1 ])
91+ }
92+
93+ scope .SetContext ("arguments" , argMap )
94+ })
95+ }
96+
97+ func sentryHub (tags map [string ]string , version string ) (* sentry.Hub , error ) {
98+ hub := sentry .CurrentHub ()
99+
100+ if hub .Client () == nil {
101+ client , err := newSentryClient (version )
102+ if err != nil {
103+ return nil , fmt .Errorf ("cannot create sentry client: %w" , err )
104+ }
105+ hub .BindClient (client )
106+ configureSentryScope (tags )
55107 }
56108
57- client .SetTagsContext (tagsContext )
109+ return hub , nil
110+ }
58111
59- return client , nil
112+ // Filter the stack frames so that the top frame is the one causing panic.
113+ // On top of the culprit one there should be
114+ // - the deferred recover function
115+ // - The two functions called in this package
116+ func filterStackFrames (event * sentry.Event ) {
117+ for _ , e := range event .Exception {
118+ if e .Stacktrace == nil {
119+ continue
120+ }
121+ frames := e .Stacktrace .Frames [:0 ]
122+ for _ , frame := range e .Stacktrace .Frames {
123+ if frame .Module == "main" && strings .HasPrefix (frame .Function , "cleanup" ) {
124+ continue
125+ }
126+ if frame .Module == "github.com/scaleway/scaleway-cli/v2/internal/sentry" && strings .HasPrefix (frame .Function , "RecoverPanicAndSendReport" ) {
127+ continue
128+ }
129+ if frame .Module == "github.com/scaleway/scaleway-cli/v2/internal/sentry" && strings .HasPrefix (frame .Function , "logAndSentry" ) {
130+ continue
131+ }
132+ frames = append (frames , frame )
133+ }
134+ e .Stacktrace .Frames = frames
135+ }
60136}
0 commit comments