JustAnalytics for Gin (Go)
Step-by-step guide to add analytics, error tracking, and APM to your Gin app
JustAnalytics for Gin (Go)
Add distributed tracing, error tracking, structured logging, and infrastructure metrics to your Gin application.
Time to data: 3-5 minutesPrerequisites#
- Go 1.21+
- Gin v1.9+
- A JustAnalytics account with a Site ID and API key
Install the SDK
go get github.com/justanalyticsapp/justanalytics-go
Initialize the SDK
package main
import (
"log"
"os"
"github.com/gin-gonic/gin"
justanalytics "github.com/justanalyticsapp/justanalytics-go"
"github.com/justanalyticsapp/justanalytics-go/middleware"
)
func main() {
err := justanalytics.Init(justanalytics.Options{
SiteID: os.Getenv("JA_SITE_ID"),
APIKey: os.Getenv("JA_API_KEY"),
ServiceName: "gin-api",
Environment: os.Getenv("GO_ENV"),
Release: os.Getenv("APP_VERSION"),
})
if err != nil {
log.Fatal(err)
}
defer justanalytics.Close()
r := gin.Default()
// Add JustAnalytics tracing middleware
r.Use(func(c *gin.Context) {
middleware.GinHandler(c.Request, c.Writer, c.FullPath(), c.Next)
})
// Your routes
r.GET("/api/users/:id", getUser)
r.POST("/api/orders", createOrder)
r.Run(":8080")
}
Add your handlers
Requests are automatically traced by the middleware. Each request creates a span named after the Gin route pattern:
func getUser(c *gin.Context) {
// Automatically traced as "GET /api/users/:id"
id := c.Param("id")
user, err := db.FindUser(c.Request.Context(), id)
if err != nil {
c.JSON(404, gin.H{"error": "User not found"})
return
}
c.JSON(200, user)
}
func createOrder(c *gin.Context) {
var req CreateOrderRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
order, err := db.CreateOrder(c.Request.Context(), req)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to create order"})
return
}
c.JSON(201, order)
}
Add error tracking
Capture errors explicitly in your handlers:
func processPayment(c *gin.Context) {
ctx := c.Request.Context()
charge, err := stripe.CreateCharge(ctx, amount)
if err != nil {
justanalytics.CaptureException(ctx, err,
justanalytics.WithTags(map[string]string{"module": "payments"}),
justanalytics.WithExtra(map[string]interface{}{"amount": amount}),
)
c.JSON(500, gin.H{"error": "Payment failed"})
return
}
c.JSON(200, charge)
}
Add a global recovery middleware:
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
if err, ok := recovered.(error); ok {
justanalytics.CaptureException(c.Request.Context(), err)
} else {
justanalytics.CaptureMessage(c.Request.Context(),
fmt.Sprintf("Panic: %v", recovered),
justanalytics.ErrorLevelFatal,
)
}
c.AbortWithStatusJSON(500, gin.H{"error": "Internal server error"})
}))
Verify data is flowing
Run your server and send a test request:
go run main.go
curl http://localhost:8080/api/users/1
Verify Your Setup
After sending a request, check the Traces tab. You should see spans with Gin route patterns like 'GET /api/users/:id'.
Open DashboardAdvanced Features#
Custom spans#
func getReport(c *gin.Context) {
ctx := c.Request.Context()
reportID := c.Param("id")
ctx, span := justanalytics.StartSpan(ctx, "generate-report",
justanalytics.WithSpanKind(justanalytics.SpanKindServer),
)
defer span.End()
span.SetAttribute("report.id", reportID)
// Nested span for database query
ctx, dbSpan := justanalytics.StartSpan(ctx, "db.fetch-report",
justanalytics.WithSpanKind(justanalytics.SpanKindClient),
)
report, err := db.GetReport(ctx, reportID)
dbSpan.End()
if err != nil {
span.SetStatus(justanalytics.SpanStatusError, err.Error())
c.JSON(500, gin.H{"error": "Failed to fetch report"})
return
}
c.JSON(200, report)
}
User identification#
// Auth middleware
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
user, err := validateToken(c.GetHeader("Authorization"))
if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
return
}
ctx := justanalytics.SetUser(c.Request.Context(), justanalytics.UserInfo{
ID: user.ID,
Email: user.Email,
})
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
Outgoing HTTP tracing#
Wrap your HTTP client transport to automatically trace outgoing requests:
client := &http.Client{
Transport: middleware.HTTPTransport(http.DefaultTransport),
}
// All outgoing requests now propagate W3C traceparent headers
resp, err := client.Get("https://api.stripe.com/v1/charges")
Database instrumentation#
import "github.com/justanalyticsapp/justanalytics-go/instrumentation"
db, _ := sql.Open("postgres", dsn)
wdb := instrumentation.WrapDB(db, "mydb")
// All queries are now traced
rows, err := wdb.QueryContext(ctx, "SELECT * FROM users WHERE id = $1", id)
Structured logging#
justanalytics.RecordLog(ctx, "info", "User logged in",
map[string]interface{}{"userId": "u123"},
)
justanalytics.RecordLog(ctx, "error", "Payment failed",
map[string]interface{}{"orderId": "o456", "reason": "declined"},
)
Custom metrics#
justanalytics.RecordMetric("custom.goroutines", float64(runtime.NumGoroutine()),
map[string]interface{}{"service": "gin-api"},
)
justanalytics.RecordMetric("custom.queue_size", float64(queue.Len()), nil)
Environment variables#
| Variable | Description | Required |
|----------|-------------|----------|
| JA_SITE_ID | Your site ID from the dashboard | Yes |
| JA_API_KEY | API key (starts with ja_sk_) | Yes |
| GO_ENV | Deployment environment | No |
| APP_VERSION | Release version for tracking | No |