From 81e3d2fa87949d57ea663adfc922348251bc620a Mon Sep 17 00:00:00 2001 From: Matt Quinn Date: Tue, 13 Oct 2026 10:28:07 -0400 Subject: [PATCH] ref(sdk): Never use the global Hub outside main() --- .golangci.yml | 7 +++++++ cmd/vroom/chunk.go | 4 ++-- cmd/vroom/main.go | 32 +++++++++++++++++++------------- cmd/vroom/profile.go | 4 ++-- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 77857119..433f6f77 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -38,6 +38,13 @@ linters: - third_party$ - builtin$ - examples$ + settings: + forbidigo: + analyze-types: true + forbid: + - pkg: github.com/getsentry/sentry-go + pattern: '^sentry\.(AddBreadcrumb|CaptureCheckIn|CaptureEvent|CaptureException|CaptureMessage|ConfigureScope|CurrentHub|Flush|FlushWithContext|LastEventID|PopScope|PushScope|Recover|RecoverWithContext|WithMonitor|WithScope)$' + msg: "Do not use the global Hub. Always use the local Hub from context." formatters: enable: - gofmt diff --git a/cmd/vroom/chunk.go b/cmd/vroom/chunk.go index fbb7eb25..1d780873 100644 --- a/cmd/vroom/chunk.go +++ b/cmd/vroom/chunk.go @@ -57,7 +57,7 @@ func (env *environment) postProfileFromChunkIDs(w http.ResponseWriter, r *http.R rawProjectID := ps.ByName("project_id") projectID, err := strconv.ParseUint(rawProjectID, 10, 64) if err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) w.WriteHeader(http.StatusBadRequest) return } @@ -248,7 +248,7 @@ func (env *environment) getRawChunk(w http.ResponseWriter, r *http.Request) { rawProjectID := ps.ByName("project_id") projectID, err := strconv.ParseUint(rawProjectID, 10, 64) if err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) w.WriteHeader(http.StatusBadRequest) return } diff --git a/cmd/vroom/main.go b/cmd/vroom/main.go index 8aa59933..f54339c6 100644 --- a/cmd/vroom/main.go +++ b/cmd/vroom/main.go @@ -42,14 +42,13 @@ var ( readJobs chan storageutil.ReadJob ) -func newEnvironment() (*environment, error) { +func newEnvironment(ctx context.Context) (*environment, error) { var e environment err := cleanenv.ReadEnv(&e.config) if err != nil { return nil, err } - ctx := context.Background() e.storage, err = blob.OpenBucket(ctx, e.config.BucketURL) if err != nil { return nil, err @@ -68,16 +67,17 @@ func newEnvironment() (*environment, error) { return &e, nil } -func (e *environment) shutdown() { +func (e *environment) shutdown(ctx context.Context) { + hub := sentry.GetHubFromContext(ctx) err := e.storage.Close() if err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) } err = e.occurrencesWriter.Close() if err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) } - sentry.Flush(5 * time.Second) + hub.Flush(5 * time.Second) } func (e *environment) newRouter() (*httprouter.Router, error) { @@ -146,7 +146,7 @@ func main() { logutil.ConfigureLogger() clearDownFile() - env, err := newEnvironment() + env, err := newEnvironment(context.Background()) if err != nil { log.Fatal("error setting up environment", err) } @@ -177,9 +177,15 @@ func main() { log.Fatal("can't initialize sentry", err) } + // This should be the only place we use the SDK's global Hub. + // Every goroutine should get its own clone instead. + // See: https://docs.sentry.io/platforms/go/usage/concurrency/ + rootHub := sentry.CurrentHub() //nolint:forbidigo + rootCtx := sentry.SetHubOnContext(context.Background(), rootHub.Clone()) + router, err := env.newRouter() if err != nil { - sentry.CaptureException(err) + rootHub.CaptureException(err) log.Fatal("error setting up the router", err) } @@ -190,7 +196,7 @@ func main() { } waitForShutdown := make(chan os.Signal) - go func() { + go func(hub *sentry.Hub) { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c @@ -199,12 +205,12 @@ func main() { defer cancel() if err := server.Shutdown(cctx); err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) slog.Error("error shutting down server", "err", err) } close(waitForShutdown) - }() + }(rootHub.Clone()) slog.Info("vroom started") @@ -215,7 +221,7 @@ func main() { err = server.ListenAndServe() if err != nil && err != http.ErrServerClosed { - sentry.CaptureException(err) + rootHub.CaptureException(err) slog.Error("server failed", "err", err) } @@ -223,7 +229,7 @@ func main() { // Shutdown the rest of the environment after the HTTP connections are closed close(readJobs) - env.shutdown() + env.shutdown(rootCtx) slog.Info("vroom graceful shutdown") } diff --git a/cmd/vroom/profile.go b/cmd/vroom/profile.go index 024cb643..f046cec9 100644 --- a/cmd/vroom/profile.go +++ b/cmd/vroom/profile.go @@ -37,7 +37,7 @@ func (env *environment) getRawProfile(w http.ResponseWriter, r *http.Request) { rawProjectID := ps.ByName("project_id") projectID, err := strconv.ParseUint(rawProjectID, 10, 64) if err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) w.WriteHeader(http.StatusBadRequest) return } @@ -116,7 +116,7 @@ func (env *environment) getProfile(w http.ResponseWriter, r *http.Request) { rawProjectID := ps.ByName("project_id") projectID, err := strconv.ParseUint(rawProjectID, 10, 64) if err != nil { - sentry.CaptureException(err) + hub.CaptureException(err) w.WriteHeader(http.StatusBadRequest) return }