Skip to content

Commit 17f829b

Browse files
maintenance/errors: Switch to sentry-go package
1 parent 93121a2 commit 17f829b

File tree

4 files changed

+123
-133
lines changed

4 files changed

+123
-133
lines changed

internal/sentry/sentry.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package sentry
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/getsentry/sentry-go"
8+
)
9+
10+
func init() {
11+
err := sentry.Init(sentry.ClientOptions{
12+
Dsn: os.Getenv("SENTRY_DSN"),
13+
Environment: os.Getenv("ENVIRONMENT"),
14+
EnableTracing: true,
15+
TracesSampleRate: 1.0,
16+
BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
17+
// Drop request body.
18+
if event.Request != nil {
19+
event.Request.Data = ""
20+
}
21+
22+
return event
23+
},
24+
})
25+
if err != nil {
26+
log.Fatalf("sentry.Init: %v", err)
27+
}
28+
}

maintenance/errors/error.go

Lines changed: 79 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -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

2224
var 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+
2731
func 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
3252
type 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

maintenance/errors/error_test.go

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ import (
99
"net/http/httptest"
1010
"strings"
1111
"testing"
12+
"time"
1213

14+
"github.com/getsentry/sentry-go"
1315
"github.com/gorilla/mux"
1416
"github.com/stretchr/testify/assert"
1517
"github.com/stretchr/testify/require"
1618

1719
"github.com/pace/bricks/http/transport"
18-
"github.com/pace/bricks/maintenance/errors/raven"
1920
"github.com/pace/bricks/maintenance/log"
2021
)
2122

@@ -61,28 +62,14 @@ func TestWrapWithExtra(t *testing.T) {
6162
}
6263
}
6364

64-
func TestStackTrace(t *testing.T) {
65-
e := sentryEvent{
66-
ctx: context.Background(),
67-
handlerName: "TestStackTrace",
68-
r: nil,
69-
level: 1,
70-
req: nil,
71-
}
72-
pak := e.build()
73-
74-
d, err := pak.JSON()
75-
assert.NoError(t, err)
76-
77-
assert.NotContains(t, string(d), `"module":"testing"`)
78-
assert.NotContains(t, string(d), `"filename":"testing/testing.go"`)
79-
}
80-
8165
func Test_createBreadcrumb(t *testing.T) {
66+
tm, err := time.Parse(time.RFC3339, "2020-02-27T10:19:28+01:00")
67+
require.NoError(t, err)
68+
8269
tests := []struct {
8370
name string
8471
data map[string]interface{}
85-
want *raven.Breadcrumb
72+
want *sentry.Breadcrumb
8673
wantErr bool
8774
}{
8875
{
@@ -93,10 +80,10 @@ func Test_createBreadcrumb(t *testing.T) {
9380
"time": "2020-02-27T10:19:28+01:00",
9481
"req_id": "bpboj6bipt34r4teo7g0",
9582
},
96-
want: &raven.Breadcrumb{
83+
want: &sentry.Breadcrumb{
9784
Level: "error",
9885
Message: "this is an error message",
99-
Timestamp: 1582795168,
86+
Timestamp: tm,
10087
Data: map[string]interface{}{},
10188
},
10289
},
@@ -115,11 +102,11 @@ func Test_createBreadcrumb(t *testing.T) {
115102
"url": "https://www.pace.car/",
116103
"req_id": "bpboj6bipt34r4teo7g0",
117104
},
118-
want: &raven.Breadcrumb{
105+
want: &sentry.Breadcrumb{
119106
Category: "http",
120107
Level: "debug",
121108
Message: "HTTPS GET www.pace.car",
122-
Timestamp: 1582795168,
109+
Timestamp: tm,
123110
Type: "http",
124111
Data: map[string]interface{}{
125112
"method": "GET",
@@ -137,11 +124,11 @@ func Test_createBreadcrumb(t *testing.T) {
137124
"message": "this is a panic message",
138125
"time": "2020-02-27T10:19:28+01:00",
139126
},
140-
want: &raven.Breadcrumb{
127+
want: &sentry.Breadcrumb{
141128
Level: "fatal",
142129
Type: "error",
143130
Message: "this is a panic message",
144-
Timestamp: 1582795168,
131+
Timestamp: tm,
145132
Data: map[string]interface{}{},
146133
},
147134
},
@@ -155,10 +142,10 @@ func Test_createBreadcrumb(t *testing.T) {
155142
"time": "2020-02-27T10:19:28+01:00",
156143
"req_id": "bpboj6bipt34r4teo7g0",
157144
},
158-
want: &raven.Breadcrumb{
145+
want: &sentry.Breadcrumb{
159146
Category: "redis",
160147
Level: "info",
161-
Timestamp: 1582795168,
148+
Timestamp: tm,
162149
Message: "this is an error message",
163150
Type: "error",
164151
Data: map[string]interface{}{},

0 commit comments

Comments
 (0)