mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-21 20:56:04 +03:00
Merge pull request #78 from Neur0toxine/sentry-extended-support
easy interaction with sentry in gin handlers
This commit is contained in:
commit
e278ba58cd
63
core/middleware/sentry.go
Normal file
63
core/middleware/sentry.go
Normal file
@ -0,0 +1,63 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var ginContextSentryKey = "sentry"
|
||||
|
||||
type Sentry interface {
|
||||
CaptureException(c *gin.Context, exception error)
|
||||
CaptureMessage(c *gin.Context, message string)
|
||||
CaptureEvent(c *gin.Context, event *sentry.Event)
|
||||
}
|
||||
|
||||
func InjectSentry(sentry Sentry) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set(ginContextSentryKey, sentry)
|
||||
}
|
||||
}
|
||||
|
||||
func GetSentry(c *gin.Context) (Sentry, bool) {
|
||||
sentryValue, ok := c.Get(ginContextSentryKey)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
obj, ok := sentryValue.(Sentry)
|
||||
if !ok || obj == nil {
|
||||
return nil, false
|
||||
}
|
||||
return obj, true
|
||||
}
|
||||
|
||||
func MustGetSentry(c *gin.Context) Sentry {
|
||||
if obj, ok := GetSentry(c); ok && obj != nil {
|
||||
return obj
|
||||
}
|
||||
panic("obj not found in context")
|
||||
}
|
||||
|
||||
func CaptureException(c *gin.Context, exception error) {
|
||||
obj, found := GetSentry(c)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
obj.CaptureException(c, exception)
|
||||
}
|
||||
|
||||
func CaptureEvent(c *gin.Context, event *sentry.Event) {
|
||||
obj, found := GetSentry(c)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
obj.CaptureEvent(c, event)
|
||||
}
|
||||
|
||||
func CaptureMessage(c *gin.Context, message string) {
|
||||
obj, found := GetSentry(c)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
obj.CaptureMessage(c, message)
|
||||
}
|
105
core/middleware/sentry_test.go
Normal file
105
core/middleware/sentry_test.go
Normal file
@ -0,0 +1,105 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type SentryMiddlewaresTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestSentryMiddlewares(t *testing.T) {
|
||||
suite.Run(t, new(SentryMiddlewaresTestSuite))
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) ctx(mock Sentry) *gin.Context {
|
||||
ctx := &gin.Context{}
|
||||
InjectSentry(mock)(ctx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestGetSentry_Empty() {
|
||||
item, found := GetSentry(&gin.Context{})
|
||||
s.Assert().False(found)
|
||||
s.Assert().Nil(item)
|
||||
|
||||
item, found = GetSentry(&gin.Context{
|
||||
Keys: map[string]interface{}{
|
||||
ginContextSentryKey: &gin.Engine{},
|
||||
},
|
||||
})
|
||||
s.Assert().False(found)
|
||||
s.Assert().Nil(item)
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestMustGetSentry_Empty() {
|
||||
s.Assert().Panics(func() {
|
||||
MustGetSentry(&gin.Context{})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestGetSentry_Success() {
|
||||
item, found := GetSentry(&gin.Context{
|
||||
Keys: map[string]interface{}{
|
||||
ginContextSentryKey: &sentryMock{},
|
||||
},
|
||||
})
|
||||
s.Assert().True(found)
|
||||
s.Assert().NotNil(item)
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestMustGetSentry_Success() {
|
||||
s.Assert().NotPanics(func() {
|
||||
item := MustGetSentry(&gin.Context{
|
||||
Keys: map[string]interface{}{
|
||||
ginContextSentryKey: &sentryMock{},
|
||||
},
|
||||
})
|
||||
s.Assert().NotNil(item)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestCaptureException() {
|
||||
err := errors.New("test error")
|
||||
item := &sentryMock{}
|
||||
item.On("CaptureException", mock.AnythingOfType("*gin.Context"), err).Return()
|
||||
CaptureException(s.ctx(item), err)
|
||||
item.AssertExpectations(s.T())
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestCaptureMessage() {
|
||||
msg := "test error"
|
||||
item := &sentryMock{}
|
||||
item.On("CaptureMessage", mock.AnythingOfType("*gin.Context"), msg).Return()
|
||||
CaptureMessage(s.ctx(item), msg)
|
||||
item.AssertExpectations(s.T())
|
||||
}
|
||||
|
||||
func (s *SentryMiddlewaresTestSuite) TestCaptureEvent() {
|
||||
event := &sentry.Event{EventID: "1"}
|
||||
item := &sentryMock{}
|
||||
item.On("CaptureEvent", mock.AnythingOfType("*gin.Context"), event).Return()
|
||||
CaptureEvent(s.ctx(item), event)
|
||||
item.AssertExpectations(s.T())
|
||||
}
|
||||
|
||||
type sentryMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (s *sentryMock) CaptureException(c *gin.Context, exception error) {
|
||||
s.Called(c, exception)
|
||||
}
|
||||
|
||||
func (s *sentryMock) CaptureMessage(c *gin.Context, message string) {
|
||||
s.Called(c, message)
|
||||
}
|
||||
|
||||
func (s *sentryMock) CaptureEvent(c *gin.Context, event *sentry.Event) {
|
||||
s.Called(c, event)
|
||||
}
|
@ -126,7 +126,24 @@ func (s *Sentry) CaptureException(c *gin.Context, exception error) {
|
||||
hub.CaptureException(exception)
|
||||
return
|
||||
}
|
||||
_ = c.Error(exception)
|
||||
}
|
||||
|
||||
// CaptureMessage and send it to Sentry.
|
||||
func (s *Sentry) CaptureMessage(c *gin.Context, message string) {
|
||||
if hub := sentrygin.GetHubFromContext(c); hub != nil {
|
||||
s.setScopeTags(c, hub.Scope())
|
||||
hub.CaptureMessage(message)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureEvent and send it to Sentry.
|
||||
func (s *Sentry) CaptureEvent(c *gin.Context, event *sentry.Event) {
|
||||
if hub := sentrygin.GetHubFromContext(c); hub != nil {
|
||||
s.setScopeTags(c, hub.Scope())
|
||||
hub.CaptureEvent(event)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SentryMiddlewares contain all the middlewares required to process errors and panics and send them to the Sentry.
|
||||
@ -201,12 +218,14 @@ func (s *Sentry) exceptionCaptureMiddleware() gin.HandlerFunc { // nolint:gocogn
|
||||
for _, err := range publicErrors {
|
||||
messages[index] = err.Error()
|
||||
s.CaptureException(c, err)
|
||||
_ = c.Error(err)
|
||||
l.Error(err.Error())
|
||||
index++
|
||||
}
|
||||
|
||||
for _, err := range privateErrors {
|
||||
s.CaptureException(c, err)
|
||||
_ = c.Error(err)
|
||||
l.Error(err.Error())
|
||||
}
|
||||
|
||||
|
@ -256,7 +256,7 @@ func (s *SentryTest) TestSentry_CaptureException_Error() {
|
||||
s.sentry.CaptureException(ctx, errors.New("test error"))
|
||||
|
||||
s.Require().Nil(transport.lastEvent)
|
||||
s.Require().Len(ctx.Errors, 1)
|
||||
s.Require().Len(ctx.Errors, 0)
|
||||
}
|
||||
|
||||
func (s *SentryTest) TestSentry_CaptureException() {
|
||||
@ -275,6 +275,38 @@ func (s *SentryTest) TestSentry_CaptureException() {
|
||||
s.Assert().NotNil(transport.lastEvent.Exception[1].Stacktrace)
|
||||
}
|
||||
|
||||
func (s *SentryTest) TestSentry_CaptureEvent_Nil() {
|
||||
defer func() {
|
||||
s.Assert().Nil(recover())
|
||||
}()
|
||||
s.sentry.CaptureEvent(&gin.Context{}, nil)
|
||||
}
|
||||
|
||||
func (s *SentryTest) TestSentry_CaptureEvent_Error() {
|
||||
ctx, transport := s.ginCtxMock()
|
||||
ctx.Keys = make(map[string]interface{})
|
||||
s.sentry.CaptureEvent(ctx, &sentry.Event{})
|
||||
|
||||
s.Require().Nil(transport.lastEvent)
|
||||
s.Require().Len(ctx.Errors, 0)
|
||||
}
|
||||
|
||||
func (s *SentryTest) TestSentry_CaptureMessage_Empty() {
|
||||
defer func() {
|
||||
s.Assert().Nil(recover())
|
||||
}()
|
||||
s.sentry.CaptureMessage(&gin.Context{}, "")
|
||||
}
|
||||
|
||||
func (s *SentryTest) TestSentry_CaptureMessage_Error() {
|
||||
ctx, transport := s.ginCtxMock()
|
||||
ctx.Keys = make(map[string]interface{})
|
||||
s.sentry.CaptureMessage(ctx, "")
|
||||
|
||||
s.Require().Nil(transport.lastEvent)
|
||||
s.Require().Len(ctx.Errors, 0)
|
||||
}
|
||||
|
||||
func (s *SentryTest) TestSentry_obtainErrorLogger_Existing() {
|
||||
ctx, _ := s.ginCtxMock()
|
||||
log := testutil.NewBufferedLogger().ForHandler("component").ForConnection("conn").ForAccount("acc")
|
||||
|
Loading…
Reference in New Issue
Block a user