@@ -11,23 +11,43 @@ import (
1111 "os"
1212 "time"
1313
14+ "github.com/getsentry/sentry-go"
15+ "github.com/prometheus/client_golang/prometheus"
16+ "github.com/rs/zerolog"
17+
1418 "github.com/pace/bricks/http/jsonapi/runtime"
1519 "github.com/pace/bricks/http/oauth2"
16- "github.com/pace/bricks/maintenance/errors/raven "
20+ _ "github.com/pace/bricks/internal/sentry "
1721 "github.com/pace/bricks/maintenance/log"
18- "github.com/prometheus/client_golang/prometheus"
19- "github.com/rs/zerolog"
2022)
2123
2224var paceHTTPPanicCounter = prometheus .NewGauge (prometheus.GaugeOpts {
2325 Name : "pace_http_panic_total" ,
2426 Help : "A counter for panics intercepted while handling a request" ,
2527})
2628
29+ var DefaultClient * sentry.Client
30+
2731func init () {
2832 prometheus .MustRegister (paceHTTPPanicCounter )
2933}
3034
35+ type ErrWithExtra struct {
36+ err error
37+ extra map [string ]any
38+ }
39+
40+ func NewErrWithExtra (err error , extra map [string ]any ) ErrWithExtra {
41+ return ErrWithExtra {
42+ err : err ,
43+ extra : extra ,
44+ }
45+ }
46+
47+ func (e ErrWithExtra ) Error () string {
48+ return e .err .Error ()
49+ }
50+
3151// PanicWrap wraps a panic for HandleRequest
3252type PanicWrap struct {
3353 err interface {}
@@ -106,7 +126,7 @@ func HandleError(rp interface{}, handlerName string, w http.ResponseWriter, r *h
106126 }
107127 log .Stack (ctx )
108128
109- sentryEvent { ctx , r , rp , 1 , handlerName }. Send ( )
129+ sentry . CaptureEvent ( getEvent ( ctx , r , rp , 1 , handlerName ) )
110130
111131 runtime .WriteError (w , http .StatusInternalServerError , errors .New ("Internal Server Error" ))
112132}
@@ -122,119 +142,94 @@ func Handle(ctx context.Context, rp interface{}) {
122142 }
123143 log .Stack (ctx )
124144
125- sentryEvent {ctx , nil , rp , 1 , "" }.Send ()
126- }
127-
128- // HandleWithCtx should be called with defer to recover panics in goroutines
129- func HandleWithCtx (ctx context.Context , handlerName string ) {
130- if rp := recover (); rp != nil {
131- log .Ctx (ctx ).Error ().Str ("handler" , handlerName ).Msgf ("Panic: %v" , rp )
132- log .Stack (ctx )
133-
134- sentryEvent {ctx , nil , rp , 2 , handlerName }.Send ()
135- }
136- }
137-
138- func HandleErrorNoStack (ctx context.Context , err error ) {
139- log .Ctx (ctx ).Info ().Msgf ("Received error, will not handle further: %v" , err )
140- }
141-
142- // New returns an error that formats as the given text.
143- func New (text string ) error {
144- return errors .New (text )
145- }
146-
147- // WrapWithExtra adds extra data to an error before reporting to Sentry
148- func WrapWithExtra (err error , extraInfo map [string ]interface {}) error {
149- return raven .WrapWithExtra (err , extraInfo )
150- }
151-
152- type sentryEvent struct {
153- ctx context.Context
154- req * http.Request // optional
155- r interface {}
156- level int
157- handlerName string
158- }
159-
160- func (e sentryEvent ) Send () {
161- _ , errCh := raven .Capture (e .build (), nil )
162- <- errCh // ensure the message get send even if the main goroutine is about to stop
145+ sentry .CaptureEvent (getEvent (ctx , nil , rp , 1 , "" ))
163146}
164147
165- func (e sentryEvent ) build () * raven.Packet {
166- ctx , r , rp , handlerName := e .ctx , e .req , e .r , e .handlerName
167-
148+ func getEvent (ctx context.Context , r * http.Request , rp any , level int , handlerName string ) * sentry.Event {
168149 // get request from context if available
169150 if r == nil {
170151 r = requestFromContext (ctx )
171152 }
172153
173154 rvalStr := fmt .Sprint (rp )
174- var packet * raven.Packet
155+
156+ event := sentry .NewEvent ()
175157
176158 if err , ok := rp .(error ); ok {
177- stack := raven .GetOrNewStacktrace (err , 2 + e .level , 3 , nil )
178- packet = raven .NewPacket (rvalStr , raven .NewException (err , stack ))
159+ event .SetException (err , level )
179160 } else {
180- stack := raven .NewStacktrace (2 + e .level , 3 , nil )
181- packet = raven .NewPacket (rvalStr , raven .NewException (errors .New (rvalStr ), stack ))
182- }
183-
184- // extract ErrWithExtra info and append it to the packet
185- if ee , ok := rp .(raven.ErrWithExtra ); ok {
186- for k , v := range ee .ExtraInfo () {
187- packet .Extra [k ] = v
188- }
161+ event .SetException (errors .New (rvalStr ), level )
189162 }
190163
191164 // add user
192165 userID , ok := oauth2 .UserID (ctx )
193- user := raven.User {ID : userID }
194- if r != nil {
195- user .IP = log .ProxyAwareRemote (r )
196- }
197- packet .Interfaces = append (packet .Interfaces , & user )
198166 if ok {
199- packet .Tags = append (packet .Tags , raven.Tag {Key : "user_id" , Value : userID })
167+ event .User .ID = userID
168+ }
169+
170+ if r != nil {
171+ event .User .IPAddress = log .ProxyAwareRemote (r )
200172 }
201173
202174 // from context
203175 if reqID := log .RequestIDFromContext (ctx ); reqID != "" {
204- packet .Extra ["req_id" ] = reqID
205- packet .Tags = append ( packet . Tags , raven. Tag { Key : "req_id" , Value : reqID })
176+ event .Extra ["req_id" ] = reqID
177+ event .Tags [ "req_id" ] = reqID
206178 }
179+
207180 if traceID := log .TraceIDFromContext (ctx ); traceID != "" {
208- packet .Extra ["uber_trace_id" ] = traceID
209- packet .Tags = append ( packet . Tags , raven. Tag { Key : "trace_id" , Value : traceID })
181+ event .Extra ["uber_trace_id" ] = traceID
182+ event .Tags [ "trace_id" ] = traceID
210183 }
211- packet .Extra ["handler" ] = handlerName
184+
185+ event .Extra ["handler" ] = handlerName
186+
212187 if clientID , ok := oauth2 .ClientID (ctx ); ok {
213- packet .Extra ["oauth2_client_id" ] = clientID
214- }
215- if scopes := oauth2 .Scopes (ctx ); len (scopes ) > 0 {
216- packet .Extra ["oauth2_scopes" ] = scopes
188+ event .Extra ["oauth2_client_id" ] = clientID
217189 }
218190
219- // from request
220- if r != nil {
221- packet .Interfaces = append (packet .Interfaces , raven .NewHttp (r ))
191+ if scopes := oauth2 .Scopes (ctx ); len (scopes ) > 0 {
192+ event .Extra ["oauth2_scopes" ] = scopes
222193 }
223194
224195 // from env
225- packet .Extra ["microservice" ] = os .Getenv ("JAEGER_SERVICE_NAME" )
196+ event .Extra ["microservice" ] = os .Getenv ("JAEGER_SERVICE_NAME" )
226197
227198 // add breadcrumbs
228- packet .Breadcrumbs = getBreadcrumbs (ctx )
199+ event .Breadcrumbs = getBreadcrumbs (ctx )
229200
230- return packet
201+ return event
202+ }
203+
204+ // HandleWithCtx should be called with defer to recover panics in goroutines
205+ func HandleWithCtx (ctx context.Context , handlerName string ) {
206+ if rp := recover (); rp != nil {
207+ log .Ctx (ctx ).Error ().Str ("handler" , handlerName ).Msgf ("Panic: %v" , rp )
208+ log .Stack (ctx )
209+
210+ sentry .CaptureEvent (getEvent (ctx , nil , rp , 2 , handlerName ))
211+ }
212+ }
213+
214+ func HandleErrorNoStack (ctx context.Context , err error ) {
215+ log .Ctx (ctx ).Info ().Msgf ("Received error, will not handle further: %v" , err )
216+ }
217+
218+ // New returns an error that formats as the given text.
219+ func New (text string ) error {
220+ return errors .New (text )
221+ }
222+
223+ // WrapWithExtra adds extra data to an error before reporting to Sentry
224+ func WrapWithExtra (err error , extraInfo map [string ]interface {}) error {
225+ return NewErrWithExtra (err , extraInfo )
231226}
232227
233228// getBreadcrumbs takes a context and tries to extract the logs from it if it
234229// holds a log.Sink. If that's the case, the logs will all be translated
235230// to valid sentry breadcrumbs if possible. In case of a failure, the
236231// breadcrumbs will be dropped and a warning will be logged.
237- func getBreadcrumbs (ctx context.Context ) []* raven .Breadcrumb {
232+ func getBreadcrumbs (ctx context.Context ) []* sentry .Breadcrumb {
238233 sink , ok := log .SinkFromContext (ctx )
239234 if ! ok {
240235 return nil
@@ -246,7 +241,7 @@ func getBreadcrumbs(ctx context.Context) []*raven.Breadcrumb {
246241 return nil
247242 }
248243
249- result := make ([]* raven .Breadcrumb , len (data ))
244+ result := make ([]* sentry .Breadcrumb , len (data ))
250245 for i , d := range data {
251246 crumb , err := createBreadcrumb (d )
252247 if err != nil {
@@ -260,7 +255,7 @@ func getBreadcrumbs(ctx context.Context) []*raven.Breadcrumb {
260255 return result
261256}
262257
263- func createBreadcrumb (data map [string ]interface {} ) (* raven .Breadcrumb , error ) {
258+ func createBreadcrumb (data map [string ]any ) (* sentry .Breadcrumb , error ) {
264259 // remove the request id if it can still be found in the logs
265260 delete (data , "req_id" )
266261
@@ -318,11 +313,11 @@ func createBreadcrumb(data map[string]interface{}) (*raven.Breadcrumb, error) {
318313 typ = "error"
319314 }
320315
321- return & raven .Breadcrumb {
316+ return & sentry .Breadcrumb {
322317 Category : category ,
323318 Level : level ,
324319 Message : message ,
325- Timestamp : time . Unix () ,
320+ Timestamp : time ,
326321 Type : typ ,
327322 Data : data ,
328323 }, nil
@@ -331,7 +326,7 @@ func createBreadcrumb(data map[string]interface{}) (*raven.Breadcrumb, error) {
331326// translateZerologLevelToSentryLevel takes in a zerolog.Level as string
332327// and returns the equivalent sentry breadcrumb level. If the given level
333328// can't be parsed to a valid zerolog.Level an error is returned.
334- func translateZerologLevelToSentryLevel (l string ) (string , error ) {
329+ func translateZerologLevelToSentryLevel (l string ) (sentry. Level , error ) {
335330 level , err := zerolog .ParseLevel (l )
336331 if err != nil {
337332 return "" , err
0 commit comments