🐹 Go Integration Guide
Learn how to instrument your Go applications to send distributed traces to TraceKit.
TraceKit Go SDK
One SDK, Complete Observability. Replace 80+ lines of boilerplate with 3 lines of code. Get distributed tracing + code monitoring with automatic instrumentation for all major frameworks.
📦 Installation
go get github.com/Tracekit-Dev/go-sdk⚡ Quick Start (3 Lines!)
Initialize once in your main function:
package main
import (
"context"
"os"
"github.com/Tracekit-Dev/go-sdk/tracekit"
)
func main() {
// Initialize TraceKit SDK - that's it!
sdk, _ := tracekit.NewSDK(&tracekit.Config{
APIKey: os.Getenv("TRACEKIT_API_KEY"),
ServiceName: "my-service",
EnableCodeMonitoring: true, // Enable live code debugging (optional)
})
defer sdk.Shutdown(context.Background())
// Your application code...
}💡 Tip: Set EnableCodeMonitoring: true to automatically capture snapshots when errors occur. Perfect for debugging production issues! Learn more →
🚀 Add Framework Middleware (One Line!)
Gin Framework
r := gin.Default()
r.Use(sdk.GinMiddleware()) // ← All routes automatically traced!Echo Framework
e := echo.New()
e.Use(sdk.EchoMiddleware()) // ← All routes automatically traced!net/http (Standard Library)
mux.Handle("/", sdk.HTTPHandler(handler, "root")) // ← Automatically traced!💾 Instrument Data Stores (One Line Each!)
Redis
client := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
sdk.WrapRedis(client) // ← All Redis ops automatically traced!MongoDB
opts := sdk.MongoClientOptions().ApplyURI("mongodb://localhost:27017")
client, _ := mongo.Connect(ctx, opts) // ← All queries automatically traced!GORM (ORM)
db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})
sdk.TraceGormDB(db) // ← All queries automatically traced!
db.Find(&users)
db.Create(&User{Name: "John"})database/sql (PostgreSQL, MySQL, SQLite)
sqlDB, _ := sql.Open("postgres", dsn)
db := sdk.WrapDB(sqlDB, "postgresql") // ← All queries automatically traced!
rows, _ := db.QueryContext(ctx, "SELECT * FROM users")HTTP Client
client := sdk.HTTPClient(&http.Client{Timeout: 10 * time.Second})
resp, _ := client.Get("https://api.example.com") // ← Automatically traced!gRPC
// Server
server := grpc.NewServer(sdk.GRPCServerInterceptors()...)
// Client
conn, _ := grpc.Dial(addr, sdk.GRPCClientInterceptors()...)✨ Complete Example
Here's a complete working application with Gin + Redis:
package main
import (
"context"
"os"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"github.com/Tracekit-Dev/go-sdk/tracekit"
)
func main() {
// 1. Initialize SDK
sdk, _ := tracekit.NewSDK(&tracekit.Config{
APIKey: os.Getenv("TRACEKIT_API_KEY"),
ServiceName: "my-api",
})
defer sdk.Shutdown(context.Background())
// 2. Setup Redis with tracing
redisClient := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
sdk.WrapRedis(redisClient)
// 3. Setup HTTP server with tracing
r := gin.Default()
r.Use(sdk.GinMiddleware())
r.GET("/api/users", func(c *gin.Context) {
// Both endpoint and Redis calls are automatically traced!
val, _ := redisClient.Get(c, "users").Result()
c.JSON(200, gin.H{"data": val})
})
r.Run(":8080")
}✅ That's it! You now have complete distributed tracing with < 20 lines of code. All HTTP endpoints and Redis operations are automatically traced with full context propagation.
💡 Why Use the SDK?
📚 Manual Setup: The sections below show manual OpenTelemetry instrumentation for users who need fine-grained control. For most use cases, the TraceKit SDK above is recommended.
90% Automatic Tracing!
With the right libraries, most of your application will be traced automatically with minimal setup. No need to manually instrument every function.
Prerequisites
- • Go 1.19 or higher
- • An active TraceKit account
- • A generated API key from the API Keys page
🔍 What Gets Traced Automatically?
With proper setup, these operations are traced automatically with zero manual instrumentation:
| Component | Setup | Auto-Traced? |
|---|---|---|
| HTTP Endpoints | Add middleware (1 line) | ✓ Yes |
| Database Queries | GORM plugin or otelsql wrapper | ✓ Yes |
| HTTP Client Calls | Wrap http.Client transport | ✓ Yes |
| Redis Operations | Add Redis hook | ✓ Yes |
| gRPC Calls | Add interceptor | ✓ Yes |
| MongoDB Queries | Use otelmongo driver | ✓ Yes |
| Custom Business Logic | Manual spans (optional) | Manual |
📦 Installation
Install the required OpenTelemetry packages:
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
go get go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin⚙️ Basic Setup
Create a tracing initialization function in your application:
1. Create tracing.go
package tracing
import (
"context"
"crypto/tls"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)
func InitTracer(serviceName, endpoint, apiKey string, useSSL bool) (func(), error) {
ctx := context.Background()
// Configure OTLP exporter options
var opts []otlptracehttp.Option
opts = append(opts,
otlptracehttp.WithEndpoint(endpoint),
otlptracehttp.WithURLPath("/v1/traces"),
otlptracehttp.WithHeaders(map[string]string{
"X-API-Key": apiKey,
}),
)
// Configure TLS
if useSSL {
opts = append(opts, otlptracehttp.WithTLSClientConfig(&tls.Config{}))
} else {
opts = append(opts, otlptracehttp.WithInsecure())
}
// Create OTLP exporter
exporter, err := otlptracehttp.New(ctx, opts...)
if err != nil {
return nil, err
}
// Create trace provider
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(serviceName),
semconv.ServiceVersionKey.String("1.0.0"),
)),
)
// Set global trace provider
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
// Return cleanup function
return func() {
_ = tp.Shutdown(ctx)
}, nil
}2. Initialize in main.go
package main
import (
"log"
"os"
"yourapp/tracing"
)
func main() {
// Initialize OpenTelemetry tracing
cleanup, err := tracing.InitTracer(
"my-service", // Service name
"{ appURL }", // TraceKit endpoint
os.Getenv("CONTEXTIO_API_KEY"), // API key from environment
false, // Use SSL (false for local development)
)
if err != nil {
log.Fatalf("Failed to initialize tracer: %v", err)
}
defer cleanup()
// Your application code here...
}🚀 Framework Integration
TraceKit works seamlessly with popular Go web frameworks through OpenTelemetry instrumentation.
Gin Framework
For Gin, use the otelgin middleware:
package main
import (
"github.com/gin-gonic/gin"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
// Initialize tracing (as shown above)
cleanup, _ := tracing.InitTracer(...)
defer cleanup()
// Create Gin router
r := gin.Default()
// Add OpenTelemetry middleware
r.Use(otelgin.Middleware("my-service"))
// Define routes - they're automatically traced!
r.GET("/api/users", func(c *gin.Context) {
c.JSON(200, gin.H{"users": []string{"alice", "bob"}})
})
r.Run(":8080")
}net/http (Standard Library)
For standard library HTTP servers:
package main
import (
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func main() {
// Initialize tracing (as shown above)
cleanup, _ := tracing.InitTracer(...)
defer cleanup()
// Wrap your handlers with otelhttp
mux := http.NewServeMux()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
mux.Handle("/", otelhttp.NewHandler(handler, "root"))
http.ListenAndServe(":8080", mux)
}Echo Framework
For Echo, use the otelecho middleware:
package main
import (
"github.com/labstack/echo/v4"
"go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho"
)
func main() {
// Initialize tracing (as shown above)
cleanup, _ := tracing.InitTracer(...)
defer cleanup()
e := echo.New()
// Add OpenTelemetry middleware
e.Use(otelecho.Middleware("my-service"))
e.GET("/", func(c echo.Context) error {
return c.String(200, "Hello, World!")
})
e.Start(":8080")
}⚡ Automatic Instrumentation Libraries
These libraries automatically create child spans for common operations. Set them up once, and every call is traced automatically.
Database Queries
Automatically trace all database operations:
GORM (PostgreSQL, MySQL, SQLite)
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
"go.opentelemetry.io/contrib/instrumentation/gorm.io/otelgorm"
)
func initDB() (*gorm.DB, error) {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
// Add OpenTelemetry plugin
if err := db.Use(otelgorm.NewPlugin()); err != nil {
return nil, err
}
return db, nil
}Now every db.Find(), db.Create(), db.Update() call creates a span automatically!
database/sql with pgx
import (
"database/sql"
"github.com/jackc/pgx/v5/stdlib"
"github.com/XSAM/otelsql"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)
func initDB() (*sql.DB, error) {
// Register with OpenTelemetry instrumentation
driverName, err := otelsql.Register("pgx",
otelsql.WithAttributes(semconv.DBSystemPostgreSQL),
)
if err != nil {
return nil, err
}
db, err := sql.Open(driverName, dsn)
if err != nil {
return nil, err
}
return db, nil
}HTTP Client Calls
Automatically trace all outgoing HTTP requests:
import (
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
// Create an HTTP client with OpenTelemetry transport (one-time setup)
var httpClient = &http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport),
}
// Now use it for all HTTP calls - automatically traced!
func callExternalAPI(ctx context.Context) (*Response, error) {
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com/users", nil)
// This HTTP call is automatically traced as a child span
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Parse response...
return parseResponse(resp)
}Every HTTP call made with this client is automatically traced, including request/response details. The spans will appear as children of your current operation.
Redis Operations
Trace Redis commands automatically with go-redis:
import (
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/extra/redisotel/v9"
)
func initRedis() *redis.Client {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// Enable OpenTelemetry instrumentation
if err := redisotel.InstrumentTracing(rdb); err != nil {
panic(err)
}
return rdb
}
// Usage - all operations are automatically traced!
func cacheUser(ctx context.Context, rdb *redis.Client, userID string, data string) error {
// This Redis SET is automatically traced
return rdb.Set(ctx, "user:"+userID, data, time.Hour).Err()
}
func getUser(ctx context.Context, rdb *redis.Client, userID string) (string, error) {
// This Redis GET is automatically traced
return rdb.Get(ctx, "user:"+userID).Result()
}All Redis operations (GET, SET, HGET, etc.) are now traced automatically.
gRPC Calls
Automatically trace gRPC server and client calls:
Server Side
import (
"google.golang.org/grpc"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
)
func main() {
// Initialize tracing first...
cleanup, _ := tracing.InitTracer(...)
defer cleanup()
// Create gRPC server with OpenTelemetry interceptor
s := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)
// Register your services
pb.RegisterYourServiceServer(s, &yourService{})
// All RPC calls are now automatically traced!
s.Serve(lis)
}Client Side
import (
"google.golang.org/grpc"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
)
func createGRPCClient() (*grpc.ClientConn, error) {
// Create gRPC client with OpenTelemetry interceptor
conn, err := grpc.Dial(
"localhost:50051",
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
)
if err != nil {
return nil, err
}
// All outgoing RPC calls are automatically traced!
return conn, nil
}MongoDB Queries
Automatically trace MongoDB operations:
import (
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo"
)
func initMongo(ctx context.Context) (*mongo.Client, error) {
// Create client options with OpenTelemetry monitor
opts := options.Client().
ApplyURI("mongodb://localhost:27017").
SetMonitor(otelmongo.NewMonitor())
client, err := mongo.Connect(ctx, opts)
if err != nil {
return nil, err
}
return client, nil
}
// Usage - all operations are automatically traced!
func findUsers(ctx context.Context, client *mongo.Client) ([]User, error) {
coll := client.Database("mydb").Collection("users")
// This MongoDB query is automatically traced
cursor, err := coll.Find(ctx, bson.M{"status": "active"})
if err != nil {
return nil, err
}
var users []User
cursor.All(ctx, &users)
return users, nil
}All MongoDB queries, inserts, updates, and deletes are automatically traced.
Message Queues
Kafka (Sarama)
import (
"github.com/IBM/sarama"
"go.opentelemetry.io/contrib/instrumentation/github.com/IBM/sarama/otelsarama"
)
func createKafkaProducer() (sarama.SyncProducer, error) {
config := sarama.NewConfig()
config.Producer.Return.Successes = true
producer, err := sarama.NewSyncProducer([]string{"localhost:9092"}, config)
if err != nil {
return nil, err
}
// Wrap with OpenTelemetry instrumentation
producer = otelsarama.WrapSyncProducer(config, producer)
return producer, nil
}
// Usage - all messages are automatically traced!
func publishMessage(ctx context.Context, producer sarama.SyncProducer, msg string) error {
message := &sarama.ProducerMessage{
Topic: "user-events",
Value: sarama.StringEncoder(msg),
}
// This publish is automatically traced
_, _, err := producer.SendMessage(message)
return err
}RabbitMQ (amqp091-go)
import (
amqp "github.com/rabbitmq/amqp091-go"
"go.opentelemetry.io/contrib/instrumentation/github.com/rabbitmq/amqp091-go/otelmqp"
)
func publishToQueue(ctx context.Context, ch *amqp.Channel, msg string) error {
// Wrap channel with OpenTelemetry instrumentation
otlChannel := otelmqp.NewChannel(ch)
// Publish message - automatically traced!
err := otlChannel.PublishWithContext(
ctx,
"", // exchange
"my-queue", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(msg),
},
)
return err
}
// Consumer side
func consumeFromQueue(ctx context.Context, ch *amqp.Channel) error {
otlChannel := otelmqp.NewChannel(ch)
// Consume messages - automatically traced!
msgs, err := otlChannel.Consume(
"my-queue", // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
if err != nil {
return err
}
for msg := range msgs {
// Process message with tracing context
processMessage(msg)
}
return nil
}🚀 Local UI (Development Mode)
Debug your Go application locally without creating an account. TraceKit Local UI runs on your machine at http://localhost:9999 and automatically receives traces when you run your app in development mode.
Automatic Detection
The Go SDK automatically detects when Local UI is running on port 9999 and sends traces to both Local UI and cloud (if you have an API key configured).
Quick Start
1. Install the Local UI:
npm install -g @tracekit/local-ui2. Start the Local UI:
tracekit-local3. Run your app in development mode:
ENV=development go run main.go4. Open your browser:
http://localhost:9999Features
Auto-Detection
SDK checks for Local UI at localhost:9999 on startup
Real-Time Updates
See traces instantly with WebSocket live updates
Development Only
Only activates when ENV=development
Works Offline
No internet connection required - everything runs locally
Benefits
- See your first trace in under 60 seconds
- Debug locally without switching to the cloud dashboard
- Stay in your flow - everything runs on your machine
- Works completely offline
- Perfect for development and demos
Troubleshooting
If traces aren't appearing in Local UI, check:
- Local UI is running (
curl http://localhost:9999/api/health) ENV=developmentis set- SDK version is v0.3.2 or higher
- Check console for "🔍 Local UI detected" message
🌐 Service Discovery
TraceKit automatically instruments outgoing HTTP calls to create service dependency graphs. When your service makes an HTTP request to another service, TraceKit creates CLIENT spans and injects trace context headers.
Custom Service Name Mappings
For local development or when service names can't be inferred from hostnames, configure service name mappings:
// Initialize SDK with service name mappings
sdk, _ := tracekit.NewSDK(&tracekit.Config{
APIKey: os.Getenv("TRACEKIT_API_KEY"),
ServiceName: "my-service",
// Map localhost URLs to actual service names
ServiceNameMappings: map[string]string{
"localhost:8082": "payment-service",
"localhost:8083": "user-service",
"localhost:8084": "inventory-service",
},
})
// Now requests to localhost:8082 will show as "payment-service"
client := sdk.HTTPClient(&http.Client{})
resp, _ := client.Get("http://localhost:8082/charge")
// -> Creates CLIENT span with peer.service = "payment-service"Service Name Detection
TraceKit intelligently extracts service names from URLs:
| URL | Extracted Service Name |
|---|---|
| http://payment-service:3000 | payment-service |
| http://payment.internal | payment |
| http://payment.svc.cluster.local | payment |
| https://api.example.com | api.example.com |
Viewing Service Dependencies
Visit your TraceKit dashboard to see the Service Map - a visual graph showing which services call which, with health metrics and latency data.
🔧 Manual Instrumentation (Optional)
For custom business logic that isn't covered by auto-instrumentation libraries, you can manually create spans. This is optional and only needed for specific operations you want to measure.
package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
)
func processOrder(ctx context.Context, orderID string) error {
// Get tracer
tracer := otel.Tracer("my-service")
// Start a parent span
ctx, span := tracer.Start(ctx, "processOrder")
defer span.End()
// Add attributes to the span
span.SetAttributes(
attribute.String("order.id", orderID),
attribute.String("order.status", "processing"),
)
// Create nested child spans - they automatically inherit from parent via ctx
if err := validateOrder(ctx, orderID); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
}
if err := chargePayment(ctx, orderID); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
}
// Mark span as successful
span.SetStatus(codes.Ok, "Order processed successfully")
return nil
}
func validateOrder(ctx context.Context, orderID string) error {
tracer := otel.Tracer("my-service")
// Child span - automatically linked to parent via ctx
ctx, span := tracer.Start(ctx, "validateOrder")
defer span.End()
span.SetAttributes(attribute.String("order.id", orderID))
// Validation logic here...
span.SetStatus(codes.Ok, "Validation successful")
return nil
}
func chargePayment(ctx context.Context, orderID string) error {
tracer := otel.Tracer("my-service")
// Another child span - also linked to parent via ctx
ctx, span := tracer.Start(ctx, "chargePayment")
defer span.End()
span.SetAttributes(attribute.String("order.id", orderID))
// Payment processing logic here...
span.SetStatus(codes.Ok, "Payment successful")
return nil
}🔐 Environment Variables
Best practice: Store sensitive configuration in environment variables:
# .env
CONTEXTIO_API_KEY=ctxio_your_generated_api_key_here
CONTEXTIO_ENDPOINT={ appURL }
SERVICE_NAME=my-backend-serviceLoad in your application:
import "os"
cleanup, err := tracing.InitTracer(
os.Getenv("SERVICE_NAME"),
os.Getenv("CONTEXTIO_ENDPOINT"),
os.Getenv("CONTEXTIO_API_KEY"),
false, // or use os.Getenv("USE_SSL") == "true"
)🏭 Production Configuration
Production Checklist
- • Use HTTPS/TLS for the OTLP endpoint
- • Store API keys in a secrets manager (AWS Secrets Manager, HashiCorp Vault)
- • Set appropriate service names and versions
- • Configure resource attributes (deployment.environment, host.name, etc.)
- • Adjust sampling rates if needed for high-traffic services
🔧 Troubleshooting
Traces Not Appearing?
- Verify your API key is correct and not revoked
- Check the endpoint URL:
https://app.tracekit.dev(no http:// prefix in WithEndpoint) - Ensure
WithInsecure()is set for local development (no TLS) - Check application logs for OpenTelemetry errors
- Verify TraceKit is running and accessible at port 8081
Connection Refused Errors?
Make sure TraceKit APM is running and the endpoint is correct:
# Check if TraceKit is running
curl http://{ appURL }/
# Test OTLP endpoint (should return 400 or 401, not connection refused)
curl -X POST http://{ appURL }/v1/traces✅ Complete Example
Here's a complete working example with Gin:
package main
import (
"log"
"os"
"github.com/gin-gonic/gin"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
"yourapp/tracing"
)
func main() {
// Initialize tracing
cleanup, err := tracing.InitTracer(
"backend-api",
"{ appURL }",
os.Getenv("CONTEXTIO_API_KEY"),
false,
)
if err != nil {
log.Fatalf("Failed to initialize tracer: %v", err)
}
defer cleanup()
// Create Gin router
r := gin.Default()
r.Use(otelgin.Middleware("backend-api"))
// Routes
r.GET("/api/users", getUsers)
r.POST("/api/users", createUser)
// Start server
log.Println("Server starting on :8080")
r.Run(":8080")
}
func getUsers(c *gin.Context) {
// This endpoint is automatically traced!
c.JSON(200, gin.H{
"users": []string{"alice", "bob", "charlie"},
})
}
func createUser(c *gin.Context) {
// This endpoint is automatically traced too!
c.JSON(201, gin.H{
"message": "User created successfully",
})
}You're all set!
Your Go application is now sending traces to TraceKit. Visit the Dashboard to see your traces.
Custom Metrics
The TraceKit Go SDK includes a lightweight metrics API for tracking counters, gauges, and histograms.
Counter
Track monotonically increasing values (requests, events):
// Create a counter with optional tags
counter := sdk.Counter("http.requests.total", map[string]string{
"service": "api",
"method": "GET",
})
// Increment by 1
counter.Inc()
// Add a specific value
counter.Add(5)Gauge
Track values that can go up or down (queue size, connections):
// Create a gauge
gauge := sdk.Gauge("http.connections.active", nil)
// Set to specific value
gauge.Set(42)
// Increment/decrement
gauge.Inc()
gauge.Dec()Histogram
Track value distributions (latencies, sizes):
// Create a histogram with tags
histogram := sdk.Histogram("http.request.duration", map[string]string{
"unit": "ms",
})
// Record values
histogram.Record(45.2)
histogram.Record(123.5)Note: Metrics are automatically buffered and exported via OTLP. View them in the Metrics Explorer.
🚀 Next Steps
- • Add auto-instrumentation libraries for components you use (Redis, gRPC, MongoDB, etc.)
- • Explore your traces on the Traces page to identify performance bottlenecks
- • Optionally add custom spans for specific business logic you want to measure
- • Configure sampling for high-traffic services to reduce overhead
- • Set up alert rules to get notified when issues occur
Pro Tip
Start with framework middleware + database instrumentation. This usually gives you 80% coverage with just 2-3 lines of setup code. Add more auto-instrumentation libraries as needed.