diff --git a/CHANGELOG.md b/CHANGELOG.md index 4834318ce..25f6b7237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ - Every integration is now a separate module, reducing the binary size and number of dependencies. Once you update `sentry-go` to latest version, you'll need to `go get` the integration you want to use. For example, if you want to use the `echo` integration, you'll need to run `go get github.com/getsentry/sentry-go/echo` ([#919](github.com/getsentry/sentry-go/pull/919)). +### Features + +Add ability to override `hub` in `context` for integrations that use custom context ([#931](https://github.com/getsentry/sentry-go/pull/931)) + ## 0.30.0 The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.30.0. diff --git a/echo/sentryecho.go b/echo/sentryecho.go index f8226fb4c..875254722 100644 --- a/echo/sentryecho.go +++ b/echo/sentryecho.go @@ -135,6 +135,10 @@ func GetHubFromContext(ctx echo.Context) *sentry.Hub { return nil } +func SetHubOnContext(ctx echo.Context, hub *sentry.Hub) { + ctx.Set(valuesKey, hub) +} + // GetSpanFromContext retrieves attached *sentry.Span instance from echo.Context. // If there is no transaction on echo.Context, it will return nil. func GetSpanFromContext(ctx echo.Context) *sentry.Span { diff --git a/echo/sentryecho_test.go b/echo/sentryecho_test.go index 663847445..4eaa285ed 100644 --- a/echo/sentryecho_test.go +++ b/echo/sentryecho_test.go @@ -420,6 +420,49 @@ func TestIntegration(t *testing.T) { } } +func TestSetHubOnContext(t *testing.T) { + err := sentry.Init(sentry.ClientOptions{}) + if err != nil { + t.Fatal(err) + } + + hub := sentry.CurrentHub().Clone() + router := echo.New() + router.GET("/set-hub", func(c echo.Context) error { + sentryecho.SetHubOnContext(c, hub) + retrievedHub := sentryecho.GetHubFromContext(c) + if retrievedHub == nil { + t.Error("expecting hub to be set on context") + } + if retrievedHub != hub { + t.Error("expecting retrieved hub to be the same as the set hub") + } + return c.NoContent(http.StatusOK) + }) + + srv := httptest.NewServer(router) + defer srv.Close() + + c := srv.Client() + c.Timeout = time.Second + + req, err := http.NewRequest("GET", srv.URL+"/set-hub", nil) + if err != nil { + t.Fatal(err) + } + res, err := c.Do(req) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 200 { + t.Errorf("Status code = %d expected: %d", res.StatusCode, 200) + } + err = res.Body.Close() + if err != nil { + t.Fatal(err) + } +} + func TestGetSpanFromContext(t *testing.T) { err := sentry.Init(sentry.ClientOptions{ EnableTracing: true, diff --git a/fasthttp/sentryfasthttp.go b/fasthttp/sentryfasthttp.go index 4863bf861..9ecf8e2a4 100644 --- a/fasthttp/sentryfasthttp.go +++ b/fasthttp/sentryfasthttp.go @@ -121,6 +121,10 @@ func GetHubFromContext(ctx *fasthttp.RequestCtx) *sentry.Hub { return nil } +func SetHubOnContext(ctx *fasthttp.RequestCtx, hub *sentry.Hub) { + ctx.SetUserValue(valuesKey, hub) +} + // GetSpanFromContext retrieves attached *sentry.Span instance from *fasthttp.RequestCtx. // If there is no transaction on *fasthttp.RequestCtx, it will return nil. func GetSpanFromContext(ctx *fasthttp.RequestCtx) *sentry.Span { diff --git a/fasthttp/sentryfasthttp_test.go b/fasthttp/sentryfasthttp_test.go index bea895f1f..7fdacc0b7 100644 --- a/fasthttp/sentryfasthttp_test.go +++ b/fasthttp/sentryfasthttp_test.go @@ -491,3 +491,19 @@ func TestGetTransactionFromContext(t *testing.T) { }) } } + +func TestSetHubOnContext(t *testing.T) { + hub := sentry.NewHub(sentry.CurrentHub().Client(), sentry.NewScope()) + ctx := &fasthttp.RequestCtx{} + + sentryfasthttp.SetHubOnContext(ctx, hub) + + retrievedHub := sentryfasthttp.GetHubFromContext(ctx) + if retrievedHub == nil { + t.Fatal("expected hub to be set on context, but got nil") + } + + if !reflect.DeepEqual(hub, retrievedHub) { + t.Fatalf("expected hub to be %v, but got %v", hub, retrievedHub) + } +} diff --git a/fiber/sentryfiber.go b/fiber/sentryfiber.go index ad427c566..1736722a3 100644 --- a/fiber/sentryfiber.go +++ b/fiber/sentryfiber.go @@ -122,6 +122,10 @@ func GetHubFromContext(ctx *fiber.Ctx) *sentry.Hub { return nil } +func SetHubOnContext(ctx *fiber.Ctx, hub *sentry.Hub) { + ctx.Locals(valuesKey, hub) +} + func GetSpanFromContext(ctx *fiber.Ctx) *sentry.Span { if span, ok := ctx.Locals(transactionKey).(*sentry.Span); ok { return span diff --git a/fiber/sentryfiber_test.go b/fiber/sentryfiber_test.go index 15736a6a2..d135b122f 100644 --- a/fiber/sentryfiber_test.go +++ b/fiber/sentryfiber_test.go @@ -531,3 +531,36 @@ func TestHandlers(t *testing.T) { }) } } + +func TestSetHubOnContext(t *testing.T) { + app := fiber.New() + hub := sentry.NewHub(sentry.CurrentHub().Client(), sentry.NewScope()) + + app.Get("/test", func(c *fiber.Ctx) error { + sentryfiber.SetHubOnContext(c, hub) + retrievedHub := sentryfiber.GetHubFromContext(c) + if retrievedHub == nil { + t.Fatal("expected hub to be set on context, but got nil") + } + if !reflect.DeepEqual(hub, retrievedHub) { + t.Fatalf("expected hub to be %v, but got %v", hub, retrievedHub) + } + return nil + }) + + req, err := http.NewRequest(http.MethodGet, "/test", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("User-Agent", "fiber") + + resp, err := app.Test(req) + if err != nil { + t.Fatalf("Request failed: %s", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Fatalf("Expected status code %d, got %d", http.StatusOK, resp.StatusCode) + } +} diff --git a/gin/sentrygin.go b/gin/sentrygin.go index 1b658d351..a9269d464 100644 --- a/gin/sentrygin.go +++ b/gin/sentrygin.go @@ -141,6 +141,11 @@ func GetHubFromContext(ctx *gin.Context) *sentry.Hub { return nil } +// SetHubOnContext sets *sentry.Hub instance to gin.Context. +func SetHubOnContext(ctx *gin.Context, hub *sentry.Hub) { + ctx.Set(valuesKey, hub) +} + // GetSpanFromContext retrieves attached *sentry.Span instance from gin.Context. // If there is no transaction on echo.Context, it will return nil. func GetSpanFromContext(ctx *gin.Context) *sentry.Span { diff --git a/gin/sentrygin_test.go b/gin/sentrygin_test.go index cef5aa228..3b33bc683 100644 --- a/gin/sentrygin_test.go +++ b/gin/sentrygin_test.go @@ -5,6 +5,7 @@ import ( "io" "net/http" "net/http/httptest" + "reflect" "strconv" "strings" "testing" @@ -410,3 +411,14 @@ func TestIntegration(t *testing.T) { t.Fatalf("Transaction status codes mismatch (-want +got):\n%s", diff) } } + +func TestSetHubOnContext(t *testing.T) { + hub := sentry.CurrentHub() + ctx := &gin.Context{} + sentrygin.SetHubOnContext(ctx, hub) + got := sentrygin.GetHubFromContext(ctx) + + if !reflect.DeepEqual(hub, got) { + t.Fatalf("Hub mismatch: got %v want %v", got, hub) + } +} diff --git a/iris/sentryiris.go b/iris/sentryiris.go index f89595d3e..829b0e0b2 100644 --- a/iris/sentryiris.go +++ b/iris/sentryiris.go @@ -114,6 +114,10 @@ func GetHubFromContext(ctx iris.Context) *sentry.Hub { return nil } +func SetHubOnContext(ctx iris.Context, hub *sentry.Hub) { + ctx.Values().Set(valuesKey, hub) +} + // GetSpanFromContext retrieves attached *sentry.Span instance from iris.Context. // If there is no transaction on iris.Context, it will return nil. func GetSpanFromContext(ctx iris.Context) *sentry.Span { diff --git a/iris/sentryiris_test.go b/iris/sentryiris_test.go index 35ddd6741..b1b35953d 100644 --- a/iris/sentryiris_test.go +++ b/iris/sentryiris_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "reflect" "strconv" "strings" "testing" @@ -477,3 +478,29 @@ func TestGetSpanFromContext(t *testing.T) { } } } + +func TestSetHubOnContext(t *testing.T) { + app := iris.New() + + app.Get("/with-hub", func(ctx iris.Context) { + hub := sentry.CurrentHub().Clone() + sentryiris.SetHubOnContext(ctx, hub) + + newHub := sentryiris.GetHubFromContext(ctx) + if newHub == nil { + t.Error("expecting hub to be not nil") + } + + if !reflect.DeepEqual(hub, newHub) { + t.Error("expecting hub to be the same") + } + + ctx.StatusCode(http.StatusOK) + }) + + srv := httptest.New(t, app) + + res := srv.Request(http.MethodGet, "/with-hub").Expect() + + res.Status(http.StatusOK) +}