mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-13 17:10:36 +00:00
147 lines
3.9 KiB
Go
147 lines
3.9 KiB
Go
package tracing
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
|
"go.opentelemetry.io/otel/propagation"
|
|
"go.opentelemetry.io/otel/sdk/resource"
|
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
var tracer trace.Tracer
|
|
|
|
// Config holds tracing configuration
|
|
type Config struct {
|
|
ServiceName string
|
|
ServiceVersion string
|
|
Endpoint string // OTLP endpoint (e.g., "localhost:4317")
|
|
Enabled bool
|
|
}
|
|
|
|
// InitTracer initializes the OpenTelemetry tracer
|
|
func InitTracer(config Config) (func(context.Context) error, error) {
|
|
if !config.Enabled {
|
|
// Return no-op shutdown function
|
|
return func(context.Context) error { return nil }, nil
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create OTLP exporter
|
|
client := otlptracegrpc.NewClient(
|
|
otlptracegrpc.WithEndpoint(config.Endpoint),
|
|
otlptracegrpc.WithInsecure(), // Use WithTLSCredentials in production
|
|
)
|
|
|
|
exporter, err := otlptrace.New(ctx, client)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create OTLP exporter: %w", err)
|
|
}
|
|
|
|
// Create resource
|
|
res, err := resource.New(ctx,
|
|
resource.WithAttributes(
|
|
semconv.ServiceNameKey.String(config.ServiceName),
|
|
semconv.ServiceVersionKey.String(config.ServiceVersion),
|
|
),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create resource: %w", err)
|
|
}
|
|
|
|
// Create trace provider
|
|
tp := sdktrace.NewTracerProvider(
|
|
sdktrace.WithBatcher(exporter),
|
|
sdktrace.WithResource(res),
|
|
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
|
)
|
|
|
|
// Set global trace provider
|
|
otel.SetTracerProvider(tp)
|
|
|
|
// Set global propagator
|
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
|
propagation.TraceContext{},
|
|
propagation.Baggage{},
|
|
))
|
|
|
|
// Get tracer
|
|
tracer = tp.Tracer(config.ServiceName)
|
|
|
|
// Return shutdown function
|
|
return tp.Shutdown, nil
|
|
}
|
|
|
|
// Middleware returns an HTTP middleware that creates spans for requests
|
|
func Middleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if tracer == nil {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
// Extract context from request headers
|
|
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
|
|
|
// Start span
|
|
ctx, span := tracer.Start(ctx, r.Method+" "+r.URL.Path,
|
|
trace.WithSpanKind(trace.SpanKindServer),
|
|
trace.WithAttributes(
|
|
semconv.HTTPMethodKey.String(r.Method),
|
|
semconv.HTTPURLKey.String(r.URL.String()),
|
|
semconv.HTTPTargetKey.String(r.URL.Path),
|
|
semconv.HTTPSchemeKey.String(r.URL.Scheme),
|
|
semconv.NetHostNameKey.String(r.Host),
|
|
),
|
|
)
|
|
defer span.End()
|
|
|
|
// Create new request with updated context
|
|
r = r.WithContext(ctx)
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// StartSpan starts a new span with the given name
|
|
func StartSpan(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, trace.Span) {
|
|
if tracer == nil {
|
|
return ctx, trace.SpanFromContext(ctx)
|
|
}
|
|
return tracer.Start(ctx, name, trace.WithAttributes(attrs...))
|
|
}
|
|
|
|
// SpanFromContext returns the current span from the context
|
|
func SpanFromContext(ctx context.Context) trace.Span {
|
|
return trace.SpanFromContext(ctx)
|
|
}
|
|
|
|
// AddEvent adds an event to the current span
|
|
func AddEvent(ctx context.Context, name string, attrs ...attribute.KeyValue) {
|
|
span := trace.SpanFromContext(ctx)
|
|
span.AddEvent(name, trace.WithAttributes(attrs...))
|
|
}
|
|
|
|
// SetAttributes sets attributes on the current span
|
|
func SetAttributes(ctx context.Context, attrs ...attribute.KeyValue) {
|
|
span := trace.SpanFromContext(ctx)
|
|
span.SetAttributes(attrs...)
|
|
}
|
|
|
|
// RecordError records an error on the current span
|
|
func RecordError(ctx context.Context, err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
span := trace.SpanFromContext(ctx)
|
|
span.RecordError(err)
|
|
}
|