Skip to content

Commit ac67d5e

Browse files
authored
feat: Add function wrapper to recover from panics and send them to Sentry (#113)
On-behalf-of: SAP [email protected]
1 parent 56faf44 commit ac67d5e

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

sentry/sentry.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,50 @@ func CaptureSentryError(err error, tags Tags, extras ...Extras) {
111111
}
112112
}
113113

114+
// Wrap returns a function wrapper that recovers from panics and sends them to Sentry.
115+
//
116+
// Usage:
117+
//
118+
// // synchronous usage
119+
// wrapped := sentry.Wrap(func() { /* work */ }, sentry.Tags{"component":"job"})
120+
// wrapped()
121+
//
122+
// // asynchronous usage
123+
// go sentry.Wrap(func() { /* work */ }, sentry.Tags{"component":"worker"})()
124+
//
125+
// Any panic inside the provided function will be captured and reported to Sentry
126+
// with the tag panic=true alongside any provided tags and extras.
127+
func Wrap(f func(), tags Tags, extras ...Extras) func() {
128+
return func() {
129+
defer func() {
130+
if r := recover(); r != nil {
131+
var err error
132+
switch v := r.(type) {
133+
case error:
134+
err = v
135+
default:
136+
err = fmt.Errorf("panic: %v", v)
137+
}
138+
139+
// ensure we always tag this as a panic while preserving any provided tags
140+
merged := Tags{"panic": "true"}
141+
for k, v := range tags {
142+
merged[k] = v
143+
}
144+
145+
CaptureError(err, merged, extras...)
146+
}
147+
}()
148+
149+
f()
150+
}
151+
}
152+
153+
// Go starts a goroutine that recovers from panics and sends them to Sentry.
154+
func Go(f func(), tags Tags, extras ...Extras) {
155+
go Wrap(f, tags, extras...)()
156+
}
157+
114158
// Add adds a new tag
115159
func (t Tags) Add(key, value string) {
116160
t[key] = value

sentry/sentry_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,27 @@ func TestCaptureSentryError(t *testing.T) {
3333
CaptureSentryError(err, nil)
3434
})
3535
}
36+
37+
func TestWrap_NoPanic(t *testing.T) {
38+
called := false
39+
40+
wrapped := Wrap(func() {
41+
called = true
42+
}, nil)
43+
44+
assert.NotPanics(t, func() {
45+
wrapped()
46+
})
47+
48+
assert.True(t, called, "wrapped function should be executed")
49+
}
50+
51+
func TestWrap_PanicIsRecovered(t *testing.T) {
52+
wrapped := Wrap(func() {
53+
panic("nil pointer exception")
54+
}, Tags{"component": "test"})
55+
56+
assert.NotPanics(t, func() {
57+
wrapped()
58+
})
59+
}

0 commit comments

Comments
 (0)