2022-11-29 14:34:05 +00:00
package opentelemetry
import (
"context"
"fmt"
"io"
"net"
2024-01-08 08:10:06 +00:00
"net/url"
"time"
2022-11-29 14:34:05 +00:00
"github.com/rs/zerolog/log"
2023-02-03 14:24:05 +00:00
"github.com/traefik/traefik/v3/pkg/types"
"github.com/traefik/traefik/v3/pkg/version"
2022-11-29 14:34:05 +00:00
"go.opentelemetry.io/otel"
2024-01-08 08:10:06 +00:00
"go.opentelemetry.io/otel/attribute"
2022-11-29 14:34:05 +00:00
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
2023-01-06 08:10:05 +00:00
"go.opentelemetry.io/otel/sdk/resource"
2022-11-29 14:34:05 +00:00
sdktrace "go.opentelemetry.io/otel/sdk/trace"
2024-01-08 08:10:06 +00:00
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
2022-11-29 14:34:05 +00:00
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding/gzip"
)
// Config provides configuration settings for the open-telemetry tracer.
type Config struct {
2024-02-06 09:04:05 +00:00
GRPC * types . OtelGRPC ` description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" export:"true" `
HTTP * types . OtelHTTP ` description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true" `
2022-11-29 14:34:05 +00:00
}
// SetDefaults sets the default values.
2024-02-06 09:04:05 +00:00
func ( c * Config ) SetDefaults ( ) {
c . HTTP = & types . OtelHTTP { }
c . HTTP . SetDefaults ( )
2022-11-29 14:34:05 +00:00
}
// Setup sets up the tracer.
2024-02-06 09:04:05 +00:00
func ( c * Config ) Setup ( serviceName string , sampleRate float64 , globalAttributes map [ string ] string ) ( trace . Tracer , io . Closer , error ) {
2022-11-29 14:34:05 +00:00
var (
err error
exporter * otlptrace . Exporter
)
if c . GRPC != nil {
2024-02-06 09:04:05 +00:00
exporter , err = c . setupGRPCExporter ( )
2022-11-29 14:34:05 +00:00
} else {
2024-02-06 09:04:05 +00:00
exporter , err = c . setupHTTPExporter ( )
2022-11-29 14:34:05 +00:00
}
if err != nil {
return nil , nil , fmt . Errorf ( "setting up exporter: %w" , err )
}
2024-01-08 08:10:06 +00:00
attr := [ ] attribute . KeyValue {
semconv . ServiceNameKey . String ( serviceName ) ,
semconv . ServiceVersionKey . String ( version . Version ) ,
}
2022-11-29 14:34:05 +00:00
2024-01-08 08:10:06 +00:00
for k , v := range globalAttributes {
attr = append ( attr , attribute . String ( k , v ) )
}
2022-11-29 14:34:05 +00:00
2023-01-06 08:10:05 +00:00
res , err := resource . New ( context . Background ( ) ,
2024-01-08 08:10:06 +00:00
resource . WithAttributes ( attr ... ) ,
2023-01-06 08:10:05 +00:00
resource . WithFromEnv ( ) ,
resource . WithTelemetrySDK ( ) ,
2024-01-08 08:10:06 +00:00
resource . WithOSType ( ) ,
resource . WithProcessCommandArgs ( ) ,
2023-01-06 08:10:05 +00:00
)
if err != nil {
return nil , nil , fmt . Errorf ( "building resource: %w" , err )
}
2024-01-08 08:10:06 +00:00
// Register the trace exporter with a TracerProvider, using a batch
// span processor to aggregate spans before export.
bsp := sdktrace . NewBatchSpanProcessor ( exporter )
2023-01-06 08:10:05 +00:00
tracerProvider := sdktrace . NewTracerProvider (
2024-01-08 08:10:06 +00:00
sdktrace . WithSampler ( sdktrace . TraceIDRatioBased ( sampleRate ) ) ,
2023-01-06 08:10:05 +00:00
sdktrace . WithResource ( res ) ,
2024-01-08 08:10:06 +00:00
sdktrace . WithSpanProcessor ( bsp ) ,
2023-01-06 08:10:05 +00:00
)
2024-01-08 08:10:06 +00:00
2022-11-29 14:34:05 +00:00
otel . SetTracerProvider ( tracerProvider )
2024-01-08 08:10:06 +00:00
otel . SetTextMapPropagator ( propagation . TraceContext { } )
2022-11-29 14:34:05 +00:00
log . Debug ( ) . Msg ( "OpenTelemetry tracer configured" )
2024-01-08 08:10:06 +00:00
return tracerProvider . Tracer ( "github.com/traefik/traefik" ) , & tpCloser { provider : tracerProvider } , err
2022-11-29 14:34:05 +00:00
}
2024-02-06 09:04:05 +00:00
func ( c * Config ) setupHTTPExporter ( ) ( * otlptrace . Exporter , error ) {
2024-01-08 08:10:06 +00:00
endpoint , err := url . Parse ( c . HTTP . Endpoint )
2022-11-29 14:34:05 +00:00
if err != nil {
2024-01-08 08:10:06 +00:00
return nil , fmt . Errorf ( "invalid collector endpoint %q: %w" , c . HTTP . Endpoint , err )
2022-11-29 14:34:05 +00:00
}
opts := [ ] otlptracehttp . Option {
2024-01-08 08:10:06 +00:00
otlptracehttp . WithEndpoint ( endpoint . Host ) ,
2024-02-06 09:04:05 +00:00
otlptracehttp . WithHeaders ( c . HTTP . Headers ) ,
2022-11-29 14:34:05 +00:00
otlptracehttp . WithCompression ( otlptracehttp . GzipCompression ) ,
}
2024-01-08 08:10:06 +00:00
if endpoint . Scheme == "http" {
2022-11-29 14:34:05 +00:00
opts = append ( opts , otlptracehttp . WithInsecure ( ) )
}
2024-01-08 08:10:06 +00:00
if endpoint . Path != "" {
opts = append ( opts , otlptracehttp . WithURLPath ( endpoint . Path ) )
2022-11-29 14:34:05 +00:00
}
2024-01-08 08:10:06 +00:00
if c . HTTP . TLS != nil {
tlsConfig , err := c . HTTP . TLS . CreateTLSConfig ( context . Background ( ) )
2022-11-29 14:34:05 +00:00
if err != nil {
return nil , fmt . Errorf ( "creating TLS client config: %w" , err )
}
opts = append ( opts , otlptracehttp . WithTLSClientConfig ( tlsConfig ) )
}
return otlptrace . New ( context . Background ( ) , otlptracehttp . NewClient ( opts ... ) )
}
2024-02-06 09:04:05 +00:00
func ( c * Config ) setupGRPCExporter ( ) ( * otlptrace . Exporter , error ) {
2024-01-08 08:10:06 +00:00
host , port , err := net . SplitHostPort ( c . GRPC . Endpoint )
2022-11-29 14:34:05 +00:00
if err != nil {
2024-02-06 09:04:05 +00:00
return nil , fmt . Errorf ( "invalid collector endpoint %q: %w" , c . GRPC . Endpoint , err )
2022-11-29 14:34:05 +00:00
}
opts := [ ] otlptracegrpc . Option {
otlptracegrpc . WithEndpoint ( fmt . Sprintf ( "%s:%s" , host , port ) ) ,
2024-02-06 09:04:05 +00:00
otlptracegrpc . WithHeaders ( c . GRPC . Headers ) ,
2022-11-29 14:34:05 +00:00
otlptracegrpc . WithCompressor ( gzip . Name ) ,
}
2024-01-08 08:10:06 +00:00
if c . GRPC . Insecure {
2022-11-29 14:34:05 +00:00
opts = append ( opts , otlptracegrpc . WithInsecure ( ) )
}
2024-01-08 08:10:06 +00:00
if c . GRPC . TLS != nil {
tlsConfig , err := c . GRPC . TLS . CreateTLSConfig ( context . Background ( ) )
2022-11-29 14:34:05 +00:00
if err != nil {
return nil , fmt . Errorf ( "creating TLS client config: %w" , err )
}
opts = append ( opts , otlptracegrpc . WithTLSCredentials ( credentials . NewTLS ( tlsConfig ) ) )
}
return otlptrace . New ( context . Background ( ) , otlptracegrpc . NewClient ( opts ... ) )
}
// tpCloser converts a TraceProvider into an io.Closer.
type tpCloser struct {
provider * sdktrace . TracerProvider
}
2024-01-08 08:10:06 +00:00
func ( t * tpCloser ) Close ( ) error {
if t == nil {
return nil
}
ctx , cancel := context . WithDeadline ( context . Background ( ) , time . Now ( ) . Add ( 5 * time . Second ) )
defer cancel ( )
return t . provider . Shutdown ( ctx )
2022-11-29 14:34:05 +00:00
}