SDKs
Go
Gin

Gin Integration

Capture errors, panics, and request context from Gin applications.

Installation

go get github.com/KodyDennon/statly-go

Setup

package main
 
import (
    "github.com/gin-gonic/gin"
    statly "github.com/KodyDennon/statly-go"
    statlygin "github.com/KodyDennon/statly-go/integrations/gin"
)
 
func main() {
    // Initialize SDK first
    statly.Init(statly.Options{
        DSN:         "https://[email protected]/your-org",
        Environment: "production",
    })
    defer statly.Close()
 
    r := gin.New()
 
    // Add Statly middleware
    r.Use(statlygin.Recovery(statlygin.Options{}))
    r.Use(statlygin.Logger())
 
    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello"})
    })
 
    r.Run(":8080")
}

Middleware

Recovery

Panic recovery middleware that captures panics as events.

func Recovery(options Options) gin.HandlerFunc
 
type Options struct {
    Repanic         bool          // Re-panic after capture (default: true)
    WaitForDelivery bool          // Wait for event to be sent
    Timeout         time.Duration // Delivery timeout
}

What's captured:

  • Panic value and stack trace
  • HTTP request context (method, URL, headers)
  • Client IP address
  • Request duration

Example:

r.Use(statlygin.Recovery(statlygin.Options{
    Repanic:         true,  // Re-panic to let Gin handle response
    WaitForDelivery: true,  // Wait for event to send before panicking
    Timeout:         2 * time.Second,
}))

Logger

Request logging middleware that adds breadcrumbs.

func Logger() gin.HandlerFunc

What's captured:

  • Request breadcrumb (method, URL, start time)
  • Response breadcrumb (status code, duration)
  • 4xx/5xx responses are marked with error level

Example:

r.Use(statlygin.Logger())

ErrorHandler

Captures errors added with c.Error().

func ErrorHandler() gin.HandlerFunc

Example:

r.Use(statlygin.ErrorHandler())
 
r.GET("/data", func(c *gin.Context) {
    data, err := fetchData()
    if err != nil {
        c.Error(err) // Will be captured by ErrorHandler
        c.JSON(500, gin.H{"error": "Failed to fetch data"})
        return
    }
    c.JSON(200, data)
})

What's Captured

Request Context

For each error, the middleware captures:

  • HTTP method, URL, path, full path
  • Query string and path parameters
  • Headers (sensitive ones filtered)
  • Client IP via c.ClientIP()
  • Host

Automatic Sanitization

Headers filtered:

  • Authorization
  • Cookie

Tags

Automatically set:

  • http.method: Request method
  • http.url: Request URL
  • transaction: Route path (e.g., /users/:id)

With Authentication

Set user context from your auth middleware:

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        user := getCurrentUser(c)
        if user != nil {
            statly.SetUser(statly.User{
                ID:    user.ID,
                Email: user.Email,
            })
        }
        c.Next()
    }
}
 
r.Use(statlygin.Recovery(statlygin.Options{}))
r.Use(AuthMiddleware())

Manual Capture

Capture errors manually with context:

r.POST("/orders", func(c *gin.Context) {
    var order OrderRequest
    if err := c.ShouldBindJSON(&order); err != nil {
        c.JSON(400, gin.H{"error": "Invalid request"})
        return
    }
 
    result, err := processOrder(order)
    if err != nil {
        statly.CaptureExceptionWithContext(err, map[string]interface{}{
            "order_total": order.Total,
            "user_id":     c.GetString("user_id"),
        })
        c.JSON(500, gin.H{"error": "Order processing failed"})
        return
    }
 
    c.JSON(200, result)
})

Full Example

package main
 
import (
    "github.com/gin-gonic/gin"
    statly "github.com/KodyDennon/statly-go"
    statlygin "github.com/KodyDennon/statly-go/integrations/gin"
)
 
func main() {
    statly.Init(statly.Options{
        DSN:         "https://[email protected]/your-org",
        Environment: "production",
    })
    defer statly.Close()
 
    r := gin.New()
 
    // Statly middleware
    r.Use(statlygin.Recovery(statlygin.Options{
        Repanic:         true,
        WaitForDelivery: true,
    }))
    r.Use(statlygin.Logger())
    r.Use(statlygin.ErrorHandler())
 
    // Auth middleware
    r.Use(func(c *gin.Context) {
        if user := authenticate(c); user != nil {
            statly.SetUser(statly.User{
                ID:    user.ID,
                Email: user.Email,
            })
        }
        c.Next()
    })
 
    // Routes
    r.GET("/api/users", listUsers)
    r.POST("/api/orders", createOrder)
 
    r.Run(":8080")
}
 
func createOrder(c *gin.Context) {
    statly.AddBreadcrumb(statly.Breadcrumb{
        Message:  "Creating order",
        Category: "order",
    })
 
    var order Order
    if err := c.ShouldBindJSON(&order); err != nil {
        c.Error(err)
        c.JSON(400, gin.H{"error": "Invalid order"})
        return
    }
 
    result, err := processOrder(order)
    if err != nil {
        statly.CaptureException(err)
        c.JSON(500, gin.H{"error": "Processing failed"})
        return
    }
 
    c.JSON(200, result)
}