2022-11-29 14:34:05 +00:00
package opentelemetry
import (
"context"
"fmt"
"io"
"net"
"github.com/opentracing/opentracing-go"
"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"
oteltracer "go.opentelemetry.io/otel/bridge/opentracing"
"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"
2023-01-06 08:10:05 +00:00
semconv "go.opentelemetry.io/otel/semconv/v1.12.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 {
// NOTE: as no gRPC option is implemented yet, the type is empty and is used as a boolean for upward compatibility purposes.
2023-04-17 09:34:00 +00:00
GRPC * struct { } ` description:"gRPC specific configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true" `
2022-11-29 14:34:05 +00:00
Address string ` description:"Sets the address (host:port) of the collector endpoint." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" `
Path string ` description:"Sets the URL path of the collector endpoint." json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true" `
Insecure bool ` description:"Disables client transport security for the exporter." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true" `
Headers map [ string ] string ` description:"Defines additional headers to be sent with the payloads." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true" `
TLS * types . ClientTLS ` description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true" `
}
// SetDefaults sets the default values.
func ( c * Config ) SetDefaults ( ) {
c . Address = "localhost:4318"
}
// Setup sets up the tracer.
func ( c * Config ) Setup ( componentName string ) ( opentracing . Tracer , io . Closer , error ) {
var (
err error
exporter * otlptrace . Exporter
)
if c . GRPC != nil {
exporter , err = c . setupGRPCExporter ( )
} else {
exporter , err = c . setupHTTPExporter ( )
}
if err != nil {
return nil , nil , fmt . Errorf ( "setting up exporter: %w" , err )
}
bt := oteltracer . NewBridgeTracer ( )
bt . SetTextMapPropagator ( propagation . NewCompositeTextMapPropagator ( propagation . TraceContext { } , propagation . Baggage { } ) )
bt . SetOpenTelemetryTracer ( otel . Tracer ( componentName , trace . WithInstrumentationVersion ( version . Version ) ) )
opentracing . SetGlobalTracer ( bt )
2023-01-06 08:10:05 +00:00
res , err := resource . New ( context . Background ( ) ,
resource . WithAttributes ( semconv . ServiceNameKey . String ( "traefik" ) ) ,
resource . WithAttributes ( semconv . ServiceVersionKey . String ( version . Version ) ) ,
resource . WithFromEnv ( ) ,
resource . WithTelemetrySDK ( ) ,
)
if err != nil {
return nil , nil , fmt . Errorf ( "building resource: %w" , err )
}
tracerProvider := sdktrace . NewTracerProvider (
sdktrace . WithResource ( res ) ,
sdktrace . WithBatcher ( exporter ) ,
)
2022-11-29 14:34:05 +00:00
otel . SetTracerProvider ( tracerProvider )
log . Debug ( ) . Msg ( "OpenTelemetry tracer configured" )
return bt , tpCloser { provider : tracerProvider } , err
}
func ( c * Config ) setupHTTPExporter ( ) ( * otlptrace . Exporter , error ) {
host , port , err := net . SplitHostPort ( c . Address )
if err != nil {
return nil , fmt . Errorf ( "invalid collector address %q: %w" , c . Address , err )
}
opts := [ ] otlptracehttp . Option {
otlptracehttp . WithEndpoint ( fmt . Sprintf ( "%s:%s" , host , port ) ) ,
otlptracehttp . WithHeaders ( c . Headers ) ,
otlptracehttp . WithCompression ( otlptracehttp . GzipCompression ) ,
}
if c . Insecure {
opts = append ( opts , otlptracehttp . WithInsecure ( ) )
}
if c . Path != "" {
opts = append ( opts , otlptracehttp . WithURLPath ( c . Path ) )
}
if c . TLS != nil {
tlsConfig , err := c . TLS . CreateTLSConfig ( context . Background ( ) )
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 ... ) )
}
func ( c * Config ) setupGRPCExporter ( ) ( * otlptrace . Exporter , error ) {
host , port , err := net . SplitHostPort ( c . Address )
if err != nil {
return nil , fmt . Errorf ( "invalid collector address %q: %w" , c . Address , err )
}
opts := [ ] otlptracegrpc . Option {
otlptracegrpc . WithEndpoint ( fmt . Sprintf ( "%s:%s" , host , port ) ) ,
otlptracegrpc . WithHeaders ( c . Headers ) ,
otlptracegrpc . WithCompressor ( gzip . Name ) ,
}
if c . Insecure {
opts = append ( opts , otlptracegrpc . WithInsecure ( ) )
}
if c . TLS != nil {
tlsConfig , err := c . TLS . CreateTLSConfig ( context . Background ( ) )
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
}
func ( t tpCloser ) Close ( ) error {
return t . provider . Shutdown ( context . Background ( ) )
}