A simplified, opinionated wrapper around OpenTelemetry tracing for Go applications. OtelKit provides an easy-to-use API for creating and managing distributed traces while hiding the complexity of the underlying OpenTelemetry SDK.
π Simple API - Easy-to-use wrapper around OpenTelemetry
π§ Flexible Configuration - Supports both simple and advanced configurations
π HTTP Middleware - Built-in middleware for popular HTTP frameworks
π Multiple Exporters - Support for Jaeger, OTLP HTTP, and OTLP gRPC
β‘ Performance Optimized - Configurable sampling and batch processing
π‘οΈ Production Ready - Comprehensive error handling and graceful shutdown
go get github.com/samims/otelkit
For most applications, use the simplified SetupTracing
approach:
package main
import (
"context"
"log"
"github.com/samims/otelkit"
"go.opentelemetry.io/otel/attribute"
)
func main() {
// Initialize tracing with sensible defaults
shutdown, err := otelkit.SetupTracing(context.Background(), "my-service", "v1.0.0")
if err != nil {
log.Fatal(err)
}
defer shutdown(context.Background())
// Create a tracer
tracer := otelkit.New("my-service")
// Create a span
ctx, span := tracer.Start(context.Background(), "do-work")
defer span.End()
// Add attributes
otelkit.AddAttributes(span,
attribute.String("user.id", "12345"),
attribute.String("operation", "process-payment"),
)
// Your business logic here
doWork(ctx)
}
func doWork(ctx context.Context) {
// Work happens here...
}
For more control, use the provider-based approach:
package main
import (
"context"
"log"
"github.com/samims/otelkit"
"go.opentelemetry.io/otel/attribute"
)
func main() {
// Initialize with provider configuration
provider, err := otelkit.NewDefaultProvider(context.Background(), "my-service", "v1.0.0")
if err != nil {
log.Fatal(err)
}
defer provider.Shutdown(context.Background())
// Create a tracer
tracer := otelkit.New("my-service")
// Create a span
ctx, span := tracer.Start(context.Background(), "do-work")
defer span.End()
// Add attributes
otelkit.AddAttributes(span,
attribute.String("user.id", "12345"),
attribute.String("operation", "process-payment"),
)
// Your business logic here
doWork(ctx)
}
func doWork(ctx context.Context) {
// Work happens here...
}
package main
import (
"context"
"net/http"
"github.com/gorilla/mux"
"github.com/samims/otelkit"
)
func main() {
// Setup tracing
provider, _ := otelkit.NewDefaultProvider(context.Background(), "web-service")
defer provider.Shutdown(context.Background())
tracer := otelkit.New("web-service")
middleware := otelkit.NewHttpMiddleware(tracer)
// Setup router with middleware
r := mux.NewRouter()
r.Use(middleware.Middleware)
r.HandleFunc("/users/{id}", getUserHandler)
http.ListenAndServe(":8080", r)
}
func getUserHandler(w http.ResponseWriter, r *http.Request) {
// Handler automatically traced by middleware
w.WriteHeader(http.StatusOK)
w.Write([]byte("User data"))
}
For production environments, you'll want more control over the configuration:
package main
import (
"context"
"time"
"github.com/samims/otelkit"
)
func main() {
// Advanced configuration
config := otelkit.NewProviderConfig("payment-service", "v2.1.0").
WithOTLPExporter("https://api.honeycomb.io", "http", false).
WithSampling("probabilistic", 0.05). // 5% sampling
WithBatchOptions(
2*time.Second, // batch timeout
10*time.Second, // export timeout
1024, // max batch size
4096, // max queue size
)
provider, err := otelkit.NewProvider(context.Background(), config)
if err != nil {
log.Fatal(err)
}
defer provider.Shutdown(context.Background())
// Use tracer as normal
tracer := otelkit.New("payment-service")
// ...
}
probabilistic
- Sample based on probability ratio (0.0 to 1.0)always_on
- Sample all traces (100%)always_off
- Sample no traces (0%)
- OTLP HTTP - HTTP-based OTLP exporter (default)
- OTLP gRPC - gRPC-based OTLP exporter (more efficient for high throughput)
Fine-tune performance with batch processor settings:
config.WithBatchOptions(
batchTimeout, // Max time to wait before exporting
exportTimeout, // Max time for export operation
maxExportBatchSize, // Max spans per batch
maxQueueSize, // Max queued spans before dropping
)
Tracer
- Main tracer wrapper with convenience methodsProviderConfig
- Configuration for tracer providerHTTPMiddleware
- HTTP middleware for automatic request tracing
SetupTracing(ctx, serviceName, serviceVersion...)
- β Recommended: Simplest setup with sensible defaultsNew(name)
- Create tracer instance for span creationNewHttpMiddleware(tracer)
- Create HTTP middleware for request tracing
NewProviderConfig(serviceName, serviceVersion)
- Create provider configurationNewProvider(ctx, config)
- Create provider with custom configuration
AddAttributes(span, ...attrs)
- Safely add attributes to spanAddEvent(span, name, ...attrs)
- Add timestamped event to spanRecordError(span, err)
- Record error and set span statusEndSpan(span)
- Safely end span
The following functions are deprecated and will be removed in v1.0.0:
SetupTracingWithDefaults()
- UseSetupTracing()
insteadMustSetupTracing()
- Handle errors explicitly insteadSetupCustomTracing()
- UseNewProviderConfig()
withNewProvider()
instead
// Simple setup with environment variables
shutdown, err := otelkit.SetupTracing(ctx, "my-service")
defer shutdown(ctx)
tracer := otelkit.New("my-service")
// Custom configuration
config := otelkit.NewProviderConfig("my-service", "v1.0.0").
WithOTLPExporter("https://api.honeycomb.io", "http", false).
WithSampling("probabilistic", 0.05)
provider, err := otelkit.NewProvider(ctx, config)
defer provider.Shutdown(ctx)
tracer := otelkit.New("my-service")
// These will be removed in v1.0.0
otelkit.SetupTracingWithDefaults(ctx, "service", "v1")
otelkit.MustSetupTracing(ctx, "service")
otelkit.SetupCustomTracing(ctx, config)
Check the /examples
directory for complete working examples:
- Basic Usage - Simple tracing setup
- HTTP Server - HTTP server with middleware
- Advanced Config - Production configuration
- Database Tracing - Database operation tracing
// Development: 100% sampling
config.WithSampling("always_on", 0)
// Production: Low sampling rate
config.WithSampling("probabilistic", 0.01) // 1%
ctx, span := tracer.Start(ctx, "operation")
defer span.End() // Always defer this
otelkit.AddAttributes(span,
attribute.String("user.id", userID),
attribute.String("operation.type", "payment"),
attribute.Int64("amount", amount),
)
if err := doSomething(); err != nil {
otelkit.RecordError(span, err)
return err
}
// Always pass context to maintain trace continuity
func processPayment(ctx context.Context, amount int64) error {
ctx, span := tracer.Start(ctx, "process-payment")
defer span.End()
return callPaymentAPI(ctx, amount) // Pass ctx along
}
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built on top of OpenTelemetry Go
- Inspired by the need for simpler tracing setup in Go applications