Files
latticelm/internal/observability/tracing.go

105 lines
2.7 KiB
Go

package observability
import (
"context"
"fmt"
"github.com/ajac-zero/latticelm/internal/config"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
// InitTracer initializes the OpenTelemetry tracer provider.
func InitTracer(cfg config.TracingConfig) (*sdktrace.TracerProvider, error) {
// Create resource with service information
res, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName(cfg.ServiceName),
),
)
if err != nil {
return nil, fmt.Errorf("failed to create resource: %w", err)
}
// Create exporter
var exporter sdktrace.SpanExporter
switch cfg.Exporter.Type {
case "otlp":
exporter, err = createOTLPExporter(cfg.Exporter)
if err != nil {
return nil, fmt.Errorf("failed to create OTLP exporter: %w", err)
}
case "stdout":
exporter, err = stdouttrace.New(
stdouttrace.WithPrettyPrint(),
)
if err != nil {
return nil, fmt.Errorf("failed to create stdout exporter: %w", err)
}
default:
return nil, fmt.Errorf("unsupported exporter type: %s", cfg.Exporter.Type)
}
// Create sampler
sampler := createSampler(cfg.Sampler)
// Create tracer provider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
sdktrace.WithSampler(sampler),
)
return tp, nil
}
// createOTLPExporter creates an OTLP gRPC exporter.
func createOTLPExporter(cfg config.ExporterConfig) (sdktrace.SpanExporter, error) {
opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(cfg.Endpoint),
}
if cfg.Insecure {
opts = append(opts, otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()))
}
if len(cfg.Headers) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(cfg.Headers))
}
// Add dial options to ensure connection
opts = append(opts, otlptracegrpc.WithDialOption(grpc.WithBlock()))
return otlptracegrpc.New(context.Background(), opts...)
}
// createSampler creates a sampler based on the configuration.
func createSampler(cfg config.SamplerConfig) sdktrace.Sampler {
switch cfg.Type {
case "always":
return sdktrace.AlwaysSample()
case "never":
return sdktrace.NeverSample()
case "probability":
return sdktrace.TraceIDRatioBased(cfg.Rate)
default:
// Default to 10% sampling
return sdktrace.TraceIDRatioBased(0.1)
}
}
// Shutdown gracefully shuts down the tracer provider.
func Shutdown(ctx context.Context, tp *sdktrace.TracerProvider) error {
if tp == nil {
return nil
}
return tp.Shutdown(ctx)
}