Gin
Send distributed traces and metrics to Levitate from Golang Gin application using OpenTelemetry
Introduction
Gin is a web framework written in Go (Golang). This comprehensive guide will help you instrument your Gin application with OpenTelemetry and smoothly send the traces to a Levitate cluster. You can also check out the example application on GitHub↗.
Pre-requisites
- You have a Gin application.
- You have signed up for Levitate, created a cluster, and obtained the following OTLP credentials from the Integrations page:
endpoint
auth_header
Install OpenTelemetry packages
To install the required packages, run the following command:
go get -u go.opentelemetry.io/otel
go get -u go.opentelemetry.io/otel/sdk
go get -u go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get -u go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin
go get -u go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc
go get -u go.opentelemetry.io/otel/sdk/metric
go get -u github.com/redis/go-redis/extra/redisotel
go get -u go.nhat.io/otelsql
Using these packages, you can instrument your Gin application to send traces and metrics to Levitate.
Traces
This application generates traces for the following:
For HTTP requests, wrap the Gin router with the otelgin.Middleware
middleware.
Refer to main.go for how to do this.
For database queries, use the otelsql
package to wrap the sql.DB
object.
Refer to
initDB()
in users/controller.go for how to do this.
For Redis commands, use the redisotel
package to wrap the redis.Client
object.
Refer to
initRedis()
in users/controller.go for how to do this.
Metrics
The application also generates metrics for the following:
- Database queries using otelsql
Set the environment variables
Set the following environment variables:
export OTEL_SERVICE_NAME=gin-app-service
export OTEL_EXPORTER_OTLP_ENDPOINT=<endpoint>
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <auth_header>"
These environment variables are used to configure the OpenTelemetry SDK to send traces and metrics to Levitate.
Instrument your application
In your Gin application, create a new file instrumentation.go
and add the following code:
Refer to main.go for how to do this.
package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
)
type Instrumentation struct {
TracerProvider *sdktrace.TracerProvider
Tracer trace.Tracer
}
func initTracerProvider() *sdktrace.TracerProvider {
exporter, err := otlptracegrpc.New(context.Background())
if err != nil {
panic(err)
}
attr := resource.WithAttributes(
semconv.DeploymentEnvironmentKey.String("production"), // You can change this value to "development" or "staging" or you can get the value from the environment variables
semconv.ServiceNameKey.String("gin-server"), // You can change this value to the name of your service
// You can add more attributes here
)
resources, err := resource.New(context.Background(),
resource.WithFromEnv(),
resource.WithTelemetrySDK(),
resource.WithProcess(),
resource.WithOS(),
resource.WithContainer(),
resource.WithHost(),
attr)
if err != nil {
panic(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resources),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
return tp
}
func NewInstrumentation() *Instrumentation {
tp := initTracerProvider()
return &Instrumentation{
TracerProvider: tp,
Tracer: tp.Tracer("gin-server"),
}
}
The above code configures the OpenTelemetry SDK to use the OTLP exporter and initializes the TracerProvider.
Next, at the entry point of your application, add the following code to instrument your application: Refer to main.go for how to do this.
i := NewInstrumentation()
defer func() {
if err := i.TracerProvider.Shutdown(context.Background()); err != nil {
log.Printf("Error shutting down tracer provider: %v", err)
}
}()
Now, add the otel gin middleware to your application:
import (
// other imports
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
// other code
r := gin.Default()
// Make sure this is added before any routes are defined
r.Use(otelgin.Middleware("gin-server"))
// other code
Refer to main.go for how to do this.
Use the tracer
object to create spans in your application as follows with custom attributes such as user id:
Refer to users/controller.go for how to do this.
_, span := u.tracer.Start(c.Request.Context(), "UpdateUser", oteltrace.WithAttributes(
attribute.String("user.id", c.Param("id")),
))
defer span.End()
Instrument the database
Add the following code to instrument the database queries.
It uses the otelsql
package to wrap the sql.DB
object and emit traces and metrics for database queries and connections.
Refer to users/controller.go for how to do this.
func initDB() (*sql.DB, error) {
driverName, err := otelsql.Register("postgres",
// Read more about the options here: https://github.com/nhatthm/otelsql?tab=readme-ov-file#options
otelsql.AllowRoot(),
otelsql.TraceQueryWithoutArgs(),
otelsql.TraceRowsClose(),
otelsql.TraceRowsAffected(),
otelsql.WithDatabaseName("otel_demo"), // database name
otelsql.WithSystem(semconv.DBSystemPostgreSQL),
)
if err != nil {
return nil, fmt.Errorf("failed to register driver: %v", err)
}
db, err := sql.Open(driverName, dsnName)
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %v", err)
}
// Record stats to expose metrics
if err := otelsql.RecordStats(db); err != nil {
return nil, err
}
return db, nil
}
Instrument the Redis
Add the following code to instrument the Redis commands.
It uses the redisotel
package to wrap the redis.Client
object and emit traces for Redis commands.
Refer to users/controller.go for how to do this.
func initRedis() *redis.Client {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Update this with your Redis server address
})
// Setup traces for redis instrumentation
if err := redisotel.InstrumentTracing(rdb); err != nil {
log.Fatalf("failed to instrument traces for Redis client: %v", err)
return nil
}
return rdb
}
Refer to users/controller.go for how to do this.
Run the application
Start your Gin application by running the following command:
go run main.go
This will start the Gin application and the OpenTelemetry SDK will automatically collect traces and metrics from the Gin application. These traces and metrics will be sent to Levitate automatically based on the environment variables set.
View traces and metrics in Levitate
After running the Gin app, you can visualize the traces and metrics in Levitate's APM dashboard.
Troubleshooting
Please get in touch with us on Discord or Email if you have any questions.