# Gin

> Instrument Gin web applications with the Last9 Go Agent for automatic HTTP request tracing and metrics

Source: https://last9.io/docs/integrations/frameworks/go/gin/

Use the Last9 Go Agent to instrument your Gin application with automatic tracing and metrics. The agent wraps the official OpenTelemetry Gin middleware under the hood — full OTel compliance with minimal code changes.

## Prerequisites

- Go 1.22 or higher
- Gin (`github.com/gin-gonic/gin`)
- Last9 account with OTLP credentials

## Installation

1. **Install the Last9 Go Agent**

   ```bash
   go get github.com/last9/go-agent
   ```

2. **Set Environment Variables**

   ```bash
   export OTEL_SERVICE_NAME="your-gin-service"
   export OTEL_EXPORTER_OTLP_ENDPOINT="$last9_otlp_endpoint"
   export OTEL_EXPORTER_OTLP_HEADERS="Authorization=$last9_otlp_auth_header"
   export OTEL_TRACES_SAMPLER="always_on"
   export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production"
   ```

3. **Instrument your application**

**New application**

   Replace `gin.Default()` with `ginagent.Default()` — that's the only change required:

   ```go
   package main

   import (
       "log"
       "net/http"

       "github.com/gin-gonic/gin"
       "github.com/last9/go-agent"
       ginagent "github.com/last9/go-agent/instrumentation/gin"
   )

   func main() {
       if err := agent.Start(); err != nil {
           log.Fatalf("failed to start agent: %v", err)
       }
       defer agent.Shutdown()

       // Drop-in replacement for gin.Default() (includes logging & recovery)
       r := ginagent.Default()

       r.GET("/", indexHandler)
       r.GET("/users/:id", getUserHandler)
       r.POST("/users", createUserHandler)

       log.Fatal(r.Run(":8080"))
   }

   func indexHandler(c *gin.Context) {
       c.JSON(http.StatusOK, gin.H{"message": "ok"})
   }

   func getUserHandler(c *gin.Context) {
       c.JSON(http.StatusOK, gin.H{"id": c.Param("id")})
   }

   func createUserHandler(c *gin.Context) {
       c.JSON(http.StatusCreated, gin.H{"status": "created"})
   }
   ```

**Minimal setup**

   Use `ginagent.New()` for a router without default middleware:

   ```go
   // No logging or recovery middleware — just tracing
   r := ginagent.New()
   ```

**Existing application**

   Add the middleware to your existing `*gin.Engine`:

   ```go
   package main

   import (
       "log"

       "github.com/gin-gonic/gin"
       "github.com/last9/go-agent"
       ginagent "github.com/last9/go-agent/instrumentation/gin"
   )

   func main() {
       if err := agent.Start(); err != nil {
           log.Fatalf("failed to start agent: %v", err)
       }
       defer agent.Shutdown()

       r := gin.New()
       r.Use(gin.Logger(), gin.Recovery())

       // Add tracing middleware to existing router
       r.Use(ginagent.Middleware())

       r.GET("/users/:id", getUserHandler)
       log.Fatal(r.Run(":8080"))
   }
   ```

## Database Instrumentation

Use the agent's database integration for automatic SQL query tracing. Supported drivers: PostgreSQL, MySQL, SQLite.

```go
import "github.com/last9/go-agent/integrations/database"

db, err := database.Open(database.Config{
    DriverName:   "postgres",
    DSN:          "postgres://user:pass@localhost/mydb",
    DatabaseName: "mydb",
})
if err != nil {
    log.Fatal(err)
}
defer db.Close()

// Use db normally — all queries are automatically traced
func getUserHandler(c *gin.Context) {
    rows, err := db.QueryContext(c.Request.Context(), "SELECT id, name FROM users WHERE id = $1", c.Param("id"))
    // ...
}
```

The agent automatically extracts `server.address`, `server.port`, `db.user`, and `db.name` from the DSN and attaches them to every span.

## Redis Instrumentation

```go
import redisagent "github.com/last9/go-agent/integrations/redis"

// Drop-in replacement for redis.NewClient()
rdb := redisagent.NewClient(&redis.Options{
    Addr: "localhost:6379",
})

func cacheHandler(c *gin.Context) {
    val, err := rdb.Get(c.Request.Context(), "key").Result()
    // ...
}
```

## Valkey Instrumentation

```go
import (
    "github.com/valkey-io/valkey-go"
    "github.com/valkey-io/valkey-go/valkeyotel"
)

client, err := valkey.NewClient(valkey.ClientOption{
    InitAddress: []string{"localhost:6379"},
})
// Wrap with OTel instrumentation
tracedClient := valkeyotel.NewClient(client)

func cacheHandler(c *gin.Context) {
    // Pass c.Request.Context() to parent the span under the HTTP request span
    val, err := tracedClient.Do(c.Request.Context(),
        tracedClient.B().Get().Key(c.Param("key")).Build(),
    ).ToString()
    // ...
}
```

See the [Valkey integration guide](/docs/integrations/frameworks/go/valkey/) for full setup details.

## DynamoDB Instrumentation

Call `last9aws.InstrumentSDK(&cfg)` once after loading your AWS config. All DynamoDB (and other AWS SDK) calls are traced automatically — no per-operation changes needed.

```go
import (
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/aws"
    dbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
    last9aws "github.com/last9/go-agent/integrations/aws"
)

// In main(), after agent.Start():
cfg, err := config.LoadDefaultConfig(ctx)
last9aws.InstrumentSDK(&cfg)
dynClient := dynamodb.NewFromConfig(cfg)

func getUserHandler(c *gin.Context) {
    // Pass c.Request.Context() to parent the span under the HTTP request span
    result, err := dynClient.GetItem(c.Request.Context(), &dynamodb.GetItemInput{
        TableName: aws.String("users"),
        Key: map[string]dbtypes.AttributeValue{
            "user_id": &dbtypes.AttributeValueMemberS{Value: c.Param("id")},
        },
    })
    // ...
}
```

See the [AWS SDK v2 integration guide](/docs/integrations/frameworks/go/aws-sdk/) for full setup details including S3, SQS, and custom attribute builders.

## HTTP Client Instrumentation

For outgoing requests with automatic `traceparent` propagation:

```go
import httpagent "github.com/last9/go-agent/integrations/http"

client := httpagent.NewClient(&http.Client{
    Timeout: 10 * time.Second,
})

func proxyHandler(c *gin.Context) {
    req, _ := http.NewRequestWithContext(c.Request.Context(), "GET", "https://upstream.example.com/api", nil)
    resp, err := client.Do(req)
    // ...
}
```

## Custom Spans

Add spans for business-critical operations:

```go
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/codes"
)

func checkoutHandler(c *gin.Context) {
    tracer := otel.Tracer("checkout")
    ctx, span := tracer.Start(c.Request.Context(), "process_payment")
    defer span.End()

    span.SetAttributes(
        attribute.String("payment.method", "card"),
        attribute.Float64("order.total", 49.99),
    )

    if err := processPayment(ctx); err != nil {
        span.RecordError(err)
        span.SetStatus(codes.Error, err.Error())
        c.JSON(http.StatusInternalServerError, gin.H{"error": "payment failed"})
        return
    }

    span.SetStatus(codes.Ok, "")
    c.JSON(http.StatusOK, gin.H{"status": "paid"})
}
```

## What Gets Traced Automatically

| Signal  | What's captured                                                    |
| ------- | ------------------------------------------------------------------ |
| Traces  | Every HTTP request: method, route pattern, status code, latency    |
| Traces  | Database queries: SQL statement, db system, server address/port    |
| Traces  | Redis commands: command name, key                                  |
| Traces  | Valkey commands: command name, key, server address/port            |
| Traces  | DynamoDB operations: table name, operation, region, request ID     |
| Traces  | Other AWS SDK calls (S3, SQS, SNS, …): service, operation, region  |
| Traces  | Outbound HTTP: method, URL, status code                            |
| Metrics | Runtime: memory, GC pause, goroutine count                         |
| Metrics | HTTP: request duration, request/response sizes, active connections |
| Metrics | Database: connection pool usage, idle, wait time                   |

## View Traces and Metrics

After running your application, navigate to [Trace Explorer](https://app.last9.io/traces) and [Metrics Explorer](https://app.last9.io/metrics) in Last9 to view your telemetry data.

---

## Troubleshooting

Please get in touch with us on [Discord](https://discord.com/invite/Q3p2EEucx9) or [Email](mailto:support@last9.io) if you have any questions.
