sentry-go

repository·master·Indexed 22 days ago

https://github.com/getsentry/sentry-go

The official Sentry SDK for the Go programming language, providing error reporting and performance monitoring. It serves as the successor to the legacy raven-go package and includes dedicated handlers for web frameworks and servers including Echo, fasthttp, Fiber (including v3), and Gin.

Tokens
51.6K
Snippets
192
Records
237
Agent score
76%

What's inside sentry-go

  1. How the Sentry Iris Hub works

    master

    The sentryiris handler attaches an instance of *sentry.Hub to the iris.Context. This ensures that Sentry data (like tags and scopes) is isolated per request and does not leak between different users or requests.

    Key Rules:

    1. Accessing the Hub: Use sentryiris.GetHubFromContext(ctx) to retrieve the hub within middleware or route handlers.
    2. Avoid Global Calls: Use the retrieved *sentry.Hub (e.g., hub.CaptureMessage) instead of global functions like sentry.CaptureMessage to maintain request-level data separation.
    3. Middleware Order: The *sentry.Hub is not available in any middleware that is attached before the sentryiris handler in the middleware chain.

    Example: Using the Hub in Middleware and Routes

    app := iris.Default()
    
    // Attach handler first
    app.Use(sentryiris.New(sentryiris.Options{
        Repanic: true,
    }))
    
    // Middleware that uses the Hub
    app.Use(func(ctx iris.Context) {
        if hub := sentryiris.GetHubFromContext(ctx); hub != nil {
            hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
        }
        ctx.Next()
    })
    
    // Route that uses the Hub
    app.Get("/", func(ctx iris.Context) {
        if hub := sentryiris.GetHubFromContext(ctx); hub != nil {
            hub.WithScope(func(scope *sentry.Scope) {
                scope.SetTag("unwantedQuery", "someQueryDataMaybe")
                hub.CaptureMessage("User provided unwanted query string, but we recovered just fine")
            })
        }
    })
    
    app.Run(iris.Addr(":3000"))
    app := iris.Default()
    
    app.Use(sentryiris.New(sentryiris.Options{
        Repanic: true,
    }))
    
    app.Use(func(ctx iris.Context) {
        if hub := sentryiris.GetHubFromContext(ctx); hub != nil {
            hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
        }
        ctx.Next()
    })
    
    app.Get("/", func(ctx iris.Context) {
        if hub := sentryiris.GetHubFromContext(ctx); hub != nil {
            hub.WithScope(func(scope *sentry.Scope) {
                scope.SetTag("unwantedQuery", "someQueryDataMaybe")
                hub.CaptureMessage("User provided unwanted query string, but we recovered just fine")
            })
        }
    })
    
    app.Run(iris.Addr(":3000"))
  2. Important notes for Sentry Zap integration

    master

    Keep the following in mind when using this integration:

    • Logs vs Events: This integration only sends logs to Sentry. It does not send Sentry events or errors. For error reporting, use the main sentry-go package.
    • Log Enabling: Logs are enabled by default. If you have DisableLogs: true in your Sentry configuration, remove it to allow log emission.
    • Flushing: Always call sentry.Flush() before your application exits to ensure all buffered logs are sent to Sentry.
  3. Access the Sentry Hub from Gin context

    master

    The sentrygin middleware attaches a *sentry.Hub to the *gin.Context. This allows you to maintain request-specific data (like tags or breadcrumbs) throughout the lifetime of a single HTTP request.

    Important:

    • Use sentrygin.GetHubFromContext(ctx) to retrieve the hub.
    • You must use the hub retrieved from the context instead of global calls like sentry.CaptureMessage or sentry.CaptureException to ensure proper data separation between concurrent requests.
    • The hub will not be available in any middleware that is attached before the sentrygin middleware.
    app.Use(sentrygin.New(sentrygin.Options{
        Repanic: true,
    }))
    
    // Subsequent middleware can access the hub
    app.Use(func(ctx *gin.Context) {
        if hub := sentrygin.GetHubFromContext(ctx); hub != nil {
            hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
        }
        ctx.Next()
    })
    
    app.GET("/", func(ctx *gin.Context) {
        if hub := sentrygin.GetHubFromContext(ctx); hub != nil {
            hub.WithScope(func(scope *sentry.Scope) {
                scope.SetTag("unwantedQuery", "someQueryDataMaybe")
                hub.CaptureMessage("User provided unwanted query string")
            })
        }
        ctx.Status(http.StatusOK
    })
  4. How Sentry gRPC interceptors work

    master

    The Sentry gRPC interceptors automatically create and manage a unique Sentry Hub for every individual gRPC request or stream. This ensures that context and breadcrumbs are isolated to the specific request lifecycle.

    To leverage this, you should:

    1. Use the Sentry SDK's context-based APIs to capture exceptions.
    2. Ensure you handle the context.Context correctly to allow tracing information to propagate across requests.
  5. How the Sentry Hub works with fasthttp context

    master

    The sentryfasthttp handler attaches a *sentry.Hub to the fasthttp.RequestCtx. This allows you to maintain request-specific data (like tags and scopes) throughout the request lifecycle.

    To access the hub in your middleware or routes, use sentryfasthttp.GetHubFromContext(ctx).

    Important:

    • You should use the hub retrieved from the context (e.g., hub.CaptureMessage) instead of global Sentry functions (e.g., sentry.CaptureMessage) to ensure proper data separation between requests.
    • The *sentry.Hub will not be available in any middleware that is attached before the sentryfasthttp handler in the middleware chain.
    func enhanceSentryEvent(handler fasthttp.RequestHandler) fasthttp.RequestHandler {
    	return func(ctx *fasthttp.RequestCtx) {
    		if hub := sentryfasthttp.GetHubFromContext(ctx); hub != nil {
    			hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
    		}
    		handler(ctx)
    	}
    }
  6. How the Sentry Hub and Request Context work

    master

    The sentryhttp handler attaches an instance of *sentry.Hub to the request's context. This allows you to maintain request-scoped data (like tags or breadcrumbs) and ensures that errors are associated with the correct request.

    Key Concepts:

    • Accessing the Hub: Use sentry.GetHubFromContext(r.Context()) to retrieve the hub within your handlers or subsequent middleware.
    • Scoped Data: Use hub.WithScope() or hub.Scope() to set tags or extra data specific to the current request.
    • Avoid Global Calls: Instead of using global functions like sentry.CaptureMessage, use the hub retrieved from the context (hub.CaptureMessage) to maintain proper data separation between concurrent requests.
    • Middleware Order: The *sentry.Hub will not be available in any middleware that is attached before the sentryhttp handler in the chain.
    func (h *handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
        // Retrieve the hub from the request context
        if hub := sentry.GetHubFromContext(r.Context()); hub != nil {
            hub.WithScope(func(scope *sentry.Scope) {
                scope.SetTag("unwantedQuery", "someQueryDataMaybe")
                hub.CaptureMessage("User provided unwanted query string")
            })
        }
        rw.WriteHeader(http.StatusOK)
    }
  7. Configure and use the Sentry fiber handler

    master

    To use the Sentry fiber handler, you must first initialize the core Sentry SDK. Then, create a new handler instance using sentryfiber.New() and attach it to your Fiber application as middleware using app.Use().

    if err := sentry.Init(sentry.ClientOptions{
    	Dsn: "your-public-dsn",
    }); err != nil {
    	fmt.Printf("Sentry initialization failed: %v\n", err)
    }
    
    // Create an instance of sentryfiber
    sentryHandler := sentryfiber.New(sentryfiber.Options{})
    
    // Attach the handler as middleware
    app := fiber.New()
    app.Use(sentryHandler)
    
    app.Listen(":3000")
  8. Use Sentry gRPC interceptors on the client side

    master

    To capture errors occurring during gRPC client calls, add sentrygrpc.UnaryClientInterceptor and sentrygrpc.StreamClientInterceptor to your grpc.NewClient (or grpc.Dial) configuration.

    import (
    	"context"
    	"fmt"
    
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    
    	"github.com/getsentry/sentry-go"
    	sentrygrpc "github.com/getsentry/sentry-go/grpc"
    )
    
    func main() {
    	// Initialize Sentry
    	if err := sentry.Init(sentry.ClientOptions{
    		Dsn: "your-public-dsn",
    	}); err != nil {
    		fmt.Printf("Sentry initialization failed: %v\n", err)
    	}
    
    	// Create gRPC client with Sentry interceptors
    	conn, err := grpc.NewClient(
    		"localhost:50051",
    		grpc.WithTransportCredentials(insecure.NewCredentials()),
    		grpc.WithUnaryInterceptor(sentrygrpc.UnaryClientInterceptor()),
    		grpc.WithStreamInterceptor(sentrygrpc.StreamClientInterceptor()),
    	)
    	if err != nil {
    		sentry.CaptureException(err)
    		fmt.Printf("Failed to connect: %v\n", err)
    		return
    	}
    	defer conn.Close()
    
    	client := NewYourServiceClient(conn)
    
    	// Make a request
    	_, err = client.YourMethod(context.Background(), &YourRequest{})
    	if err != nil {
    		sentry.CaptureException(err)
    		fmt.Printf("Error calling method: %v\n", err)
    	}
    }
  9. Install the Sentry Fiber v3 handler

    master

    To use Sentry with Fiber v3, install the fiberv3 package using go get and import it alongside github.com/gofiber/fiber/v3 and the core github.com/getsentry/sentry-go SDK.

    go get github.com/getsentry/sentry-go/fiberv3
    import (
    	"fmt"
    	fiber "github.com/gofiber/fiber/v3"
    	"github.com/getsentry/sentry-go"
    	sentryfiber "github.com/getsentry/sentry-go/fiberv3"
    )
  10. Integrate Sentry with Iris

    master

    To integrate Sentry, you must first initialize the global Sentry SDK, then attach the sentryiris handler as middleware to your Iris application.

    import (
        "fmt"
    
        "github.com/getsentry/sentry-go"
        sentryiris "github.com/getsentry/sentry-go/iris"
        "github.com/kataras/iris/v12"
    )
    
    // 1. Initialize Sentry
    if err := sentry.Init(sentry.ClientOptions{
        Dsn: "your-public-dsn",
    }); err != nil {
        fmt.Printf("Sentry initialization failed: %v\n", err)
    }
    
    app := iris.Default()
    
    // 2. Attach the handler as middleware
    app.Use(sentryiris.New(sentryiris.Options{}))
    
    // 3. Set up routes
    app.Get("/", func(ctx iris.Context) {
        ctx.Writef("Hello world!")
    })
    
    app.Run(iris.Addr(":3000"))
    import (
        "fmt"
    
        "github.com/getsentry/sentry-go"
        sentryiris "github.com/getsentry/sentry-go/iris"
        "github.com/kataras/iris/v12"
    )
    
    if err := sentry.Init(sentry.ClientOptions{
        Dsn: "your-public-dsn",
    }); err != nil {
        fmt.Printf("Sentry initialization failed: %v\n", err)
    }
    
    app := iris.Default()
    app.Use(sentryiris.New(sentryiris.Options{}))
    
    app.Get("/", func(ctx iris.Context) {
        ctx.Writef("Hello world!")
    })
    
    app.Run(iris.Addr(":3000"))
  11. Use Sentry gRPC interceptors on the server side

    master

    To capture errors and manage Sentry context for incoming gRPC requests, add sentrygrpc.UnaryServerInterceptor and sentrygrpc.StreamServerInterceptor to your grpc.NewServer configuration. You can pass sentrygrpc.ServerOptions to customize behavior, such as enabling Repanic.

    import (
    	"fmt"
    	"net"
    
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/reflection"
    
    	"github.com/getsentry/sentry-go"
    	sentrygrpc "github.com/getsentry/sentry-go/grpc"
    )
    
    func main() {
    	// Initialize Sentry
    	if err := sentry.Init(sentry.ClientOptions{
    		Dsn: "your-public-dsn",
    	}); err != nil {
    		fmt.Printf("Sentry initialization failed: %v\n", err)
    	}
    
    	// Create gRPC server with Sentry interceptors
    	server := grpc.NewServer(
    		grpc.UnaryInterceptor(sentrygrpc.UnaryServerInterceptor(sentrygrpc.ServerOptions{
    			Repanic: true,
    		})),
    		grpc.StreamInterceptor(sentrygrpc.StreamServerInterceptor(sentrygrpc.ServerOptions{
    			Repanic: true,
    		})),
    	)
    
    	// Register reflection for debugging
    	reflection.Register(server)
    
    	// Start the server
    	listener, err := net.Listen("tcp", ":50051")
    	if err != nil {
    		sentry.CaptureException(err)
    		fmt.Printf("Failed to listen: %v\n", err)
    		return
    	}
    
    	fmt.Println("Server running...")
    	if err := server.Serve(listener); err != nil {
    		sentry.CaptureException(err)
    	}
    }