Skip to content
Last9
Book demo

Valkey

Instrument Valkey clients in Go applications with automatic command tracing using valkey-go and valkeyotel

Use the valkey-go client with its built-in OpenTelemetry instrumentation (valkeyotel) to trace every Valkey command automatically. Each command produces a span with the command name, key, and server address.

Prerequisites

  • Go 1.22 or higher
  • Valkey server running
  • Last9 account with OTLP credentials

Installation

  1. Install the Last9 Go Agent and valkey-go

    go get github.com/last9/go-agent
    go get github.com/valkey-io/valkey-go
  2. Set Environment Variables

    export OTEL_SERVICE_NAME="your-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. Create an instrumented Valkey client

    package main
    import (
    "context"
    "log"
    "github.com/last9/go-agent"
    "github.com/valkey-io/valkey-go"
    "github.com/valkey-io/valkey-go/valkeyotel"
    )
    func main() {
    if err := agent.Start(); err != nil {
    log.Fatalf("failed to start agent: %v", err)
    }
    defer agent.Shutdown()
    client, err := valkey.NewClient(valkey.ClientOption{
    InitAddress: []string{"localhost:6379"},
    })
    if err != nil {
    log.Fatalf("failed to create valkey client: %v", err)
    }
    defer client.Close()
    // Wrap with OTel instrumentation — every command is now traced
    tracedClient := valkeyotel.NewClient(client)
    ctx := context.Background()
    // Commands are traced automatically
    if err := tracedClient.Do(ctx, tracedClient.B().Set().Key("hello").Value("world").Ex(60).Build()).Error(); err != nil {
    log.Printf("set failed: %v", err)
    }
    val, err := tracedClient.Do(ctx, tracedClient.B().Get().Key("hello").Build()).ToString()
    if err != nil {
    log.Printf("get failed: %v", err)
    }
    log.Println("got:", val)
    }

Using with Gin

Pass c.Request.Context() into every Valkey command so the command span is parented to the incoming HTTP request span:

package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/last9/go-agent"
ginagent "github.com/last9/go-agent/instrumentation/gin"
"github.com/valkey-io/valkey-go"
"github.com/valkey-io/valkey-go/valkeyotel"
)
var tracedClient valkey.Client
func main() {
if err := agent.Start(); err != nil {
log.Fatalf("failed to start agent: %v", err)
}
defer agent.Shutdown()
client, err := valkey.NewClient(valkey.ClientOption{
InitAddress: []string{"localhost:6379"},
})
if err != nil {
log.Fatalf("failed to create valkey client: %v", err)
}
defer client.Close()
tracedClient = valkeyotel.NewClient(client)
r := ginagent.Default()
r.GET("/cache/:key", getCacheHandler)
log.Fatal(r.Run(":8080"))
}
func getCacheHandler(c *gin.Context) {
// Pass c.Request.Context() so this span is a child of the HTTP span
val, err := tracedClient.Do(c.Request.Context(),
tracedClient.B().Get().Key(c.Param("key")).Build(),
).ToString()
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
c.JSON(http.StatusOK, gin.H{"value": val})
}

What Gets Traced Automatically

AttributeExample
db.systemvalkey
db.operationGET, SET, HGET
net.peer.namelocalhost
net.peer.port6379

View Traces

Navigate to Trace Explorer in Last9. Filter by db.system = valkey to see all Valkey commands.


Troubleshooting

Please get in touch with us on Discord or Email if you have any questions.