Skip to content

Commit

Permalink
feat: add panic propagation option to gin middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelb committed Sep 19, 2022
1 parent 99546a3 commit a37954b
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
32 changes: 23 additions & 9 deletions module/apmgin/middleware.go
Expand Up @@ -44,8 +44,9 @@ func init() {
// Use WithTracer to specify an alternative tracer.
func Middleware(engine *gin.Engine, o ...Option) gin.HandlerFunc {
m := &middleware{
engine: engine,
tracer: apm.DefaultTracer(),
engine: engine,
tracer: apm.DefaultTracer(),
panicPropagation: false,
}
for _, o := range o {
o(m)
Expand All @@ -57,9 +58,10 @@ func Middleware(engine *gin.Engine, o ...Option) gin.HandlerFunc {
}

type middleware struct {
engine *gin.Engine
tracer *apm.Tracer
requestIgnorer apmhttp.RequestIgnorerFunc
engine *gin.Engine
tracer *apm.Tracer
requestIgnorer apmhttp.RequestIgnorerFunc
panicPropagation bool
}

func (m *middleware) handle(c *gin.Context) {
Expand All @@ -75,17 +77,20 @@ func (m *middleware) handle(c *gin.Context) {

defer func() {
if v := recover(); v != nil {
if !c.Writer.Written() {
c.AbortWithStatus(http.StatusInternalServerError)
if m.panicPropagation {
defer panic(v)
} else {
c.Abort()
if !c.Writer.Written() {
c.AbortWithStatus(http.StatusInternalServerError)
} else {
c.Abort()
}
}
e := m.tracer.Recovered(v)
e.SetTransaction(tx)
setContext(&e.Context, c, body)
e.Send()
}
c.Writer.WriteHeaderNow()
tx.Result = apmhttp.StatusCodeResult(c.Writer.Status())

if tx.Sampled() {
Expand Down Expand Up @@ -144,3 +149,12 @@ func WithRequestIgnorer(r apmhttp.RequestIgnorerFunc) Option {
m.requestIgnorer = r
}
}

// WithPanicPropagation returns an Option which enable panic propagation.
// Any panic will be recovered and recorded as an error in a transaction, then
// panic will be caused again.
func WithPanicPropagation() Option {
return func(m *middleware) {
m.panicPropagation = true
}
}
19 changes: 19 additions & 0 deletions module/apmgin/middleware_test.go
Expand Up @@ -219,6 +219,25 @@ func TestMiddlewarePanicHeadersSent(t *testing.T) {
assert.NotContains(t, debugOutput.String(), "[WARNING] Headers were already written")
}

func TestMiddlewarePanicPropagation(t *testing.T) {
debugOutput.Reset()
tracer, transport := transporttest.NewRecorderTracer()
defer tracer.Close()

e := gin.New()
e.Use(
gin.CustomRecovery(func(c *gin.Context, err interface{}) {
c.AbortWithStatus(http.StatusNotImplemented)
}),
apmgin.Middleware(e, apmgin.WithTracer(tracer), apmgin.WithPanicPropagation()))
e.GET("/panic", handlePanic)

w := doRequest(e, "GET", "http://server.testing/panic")
assert.Equal(t, http.StatusNotImplemented, w.Code)
tracer.Flush(nil)
assertError(t, transport.Payloads(), "handlePanic", "boom", false)
}

func TestMiddlewareError(t *testing.T) {
debugOutput.Reset()
tracer, transport := transporttest.NewRecorderTracer()
Expand Down

0 comments on commit a37954b

Please sign in to comment.