From 1826a4b6cc125525114b20af1b2adfd22ad1dbdd Mon Sep 17 00:00:00 2001 From: viveksing Date: Thu, 9 Jan 2025 15:57:59 +0100 Subject: [PATCH] Add opentelemetry bridge - add tests --- tracing/tracers/lightstepotelbridge/README.md | 28 + tracing/tracers/lightstepotelbridge/bridge.go | 385 +++++++++ .../lightstepotelbridge/bridge_test.go | 818 ++++++++++++++++++ tracing/tracers/otel/otel.go | 311 ------- tracing/tracing.go | 6 +- 5 files changed, 1234 insertions(+), 314 deletions(-) create mode 100644 tracing/tracers/lightstepotelbridge/README.md create mode 100644 tracing/tracers/lightstepotelbridge/bridge.go create mode 100644 tracing/tracers/lightstepotelbridge/bridge_test.go delete mode 100644 tracing/tracers/otel/otel.go diff --git a/tracing/tracers/lightstepotelbridge/README.md b/tracing/tracers/lightstepotelbridge/README.md new file mode 100644 index 0000000000..95fe7bf75b --- /dev/null +++ b/tracing/tracers/lightstepotelbridge/README.md @@ -0,0 +1,28 @@ +# lightstep-otel-bridge tracer + +As with [other tracers](https://pkg.go.dev/github.com/zalando/skipper/tracing), the lightstep-otel-bridge tracer is configured by setting +`-opentracing="lightstep-otel-bridge OPTIONS"`. Valid options are: + +* `component-name` - set component name instead of `skipper` +* `access-token` - Access token for the lightstep satellites (REQUIRED) +* `protocol` - sets `UseGRPC` option to true if set to `"grpc"`, defaults to `"grpc"`, but can be set to `"http"` +* `tag` - key-value pairs (`key=value`) separated by commas (`,`) to set as tags + in every span +* `environment` - set the environment tag, defaults to `dev` +* `service-name` - set the service name tag, defaults to `skipper` +* `service-version` - set the service version tag, defaults to `unknown` +* `batch-size` - maximum number of spans to send in a batch +* `batch-timeout` - maximum time ms to wait before sending spans to the satellites +* `processor-queue-size` - maximum number of spans to queue before sending to the satellites +* `export-timeout` - maximum time to wait in ms for a batch to be sent +* `collector` - hostname (+port) - (e.g. `lightstep-satellites.example.org:4443`) to send the + spans to, i.e. your lightstep satellites +* `insecure-connection` (boolean) - force plaintext communication with satellites +* `propagators` - set propagators to use (i.e. format of http headers used for tracing). This can be used + to pick up traces from / to applications which only understand the B3 format (e.g. grafana where the + jaeger instrumentation can be switched to use the B3 format). This can be combined, e.g. `ottrace,b3` + should be used to pick up both formats (attempted in that order). + * `ottrace` - use the standard lightstep headers (default) + * `b3` - use the B3 propagation format + * `baggage` - use the baggage propagation format + * `tracecontext` - use the tracecontext propagation format diff --git a/tracing/tracers/lightstepotelbridge/bridge.go b/tracing/tracers/lightstepotelbridge/bridge.go new file mode 100644 index 0000000000..2d62b50ea3 --- /dev/null +++ b/tracing/tracers/lightstepotelbridge/bridge.go @@ -0,0 +1,385 @@ +package lightstepotelbridge + +import ( + "context" + "errors" + "fmt" + "github.com/opentracing/opentracing-go" + log "github.com/sirupsen/logrus" + "go.opentelemetry.io/contrib/propagators/b3" + ottrace "go.opentelemetry.io/contrib/propagators/ot" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + otelBridge "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" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "net" + "os" + "strconv" + "strings" + "time" +) + +const ( + defaultEndpoint = "ingest.lightstep.com:443" + defaultServiceName = "skipper" + defaultServiceVersion = "0.1.0" + defaultEnvironment = "dev" + defaultTracerName = "lightstep-otel-bridge" + defaultComponentName = "skipper" + urlPath = "traces/otlp/v0.9" + defaultPropagators = "ottrace,b3" + hostNameKey = "hostname" + lsEnvironmentKey = "environment" + defaultBatchTimeout = 2500 * time.Millisecond + defaultProcessorQueueSize = 10000 + defaultBatchSize = 512 + defaultExportTimeout = 5000 * time.Millisecond +) + +type Options struct { + Collector string + AccessToken string + Environment string + UseHttp bool + UseGrpc bool + UsePlainText bool + ComponentName string + ServiceName string + ServiceVersion string + BatchTimeout time.Duration + ProcessorQueueSize int + BatchSize int + ExportTimeout time.Duration + Hostname string + Propagators []propagation.TextMapPropagator + GlobalAttributes []attribute.KeyValue +} + +func parseOptions(opts []string) (Options, error) { + + var ( + serviceName string + serviceVersion string + endpoint string + lsToken string + lsEnvironment string + componentName string + propagators string + useHttp, useGrpc, usePlainText bool + err error + globalTags []attribute.KeyValue + hostname, _ = os.Hostname() + batchTimeout = defaultBatchTimeout + processorQueueSize = defaultProcessorQueueSize + batchSize = defaultBatchSize + exportTimeout = defaultExportTimeout + ) + + for _, o := range opts { + key, val, _ := strings.Cut(o, "=") + switch key { + case "collector": + var sport string + + _, sport, err = net.SplitHostPort(val) + if err != nil { + return Options{}, err + } + + _, err = strconv.Atoi(sport) + if err != nil { + return Options{}, fmt.Errorf("failed to parse %s as int: %w", sport, err) + } + endpoint = val + + case "access-token": + lsToken = val + case "environment": + lsEnvironment = val + case "protocol": + if strings.ToLower(val) == "http" { + useHttp = true + } else if strings.ToLower(val) == "grpc" { + useGrpc = true + } else { + return Options{}, fmt.Errorf("unsupported protocol %s", val) + } + case "insecure-connection": + usePlainText, err = strconv.ParseBool(val) + if err != nil { + return Options{}, fmt.Errorf("failed to parse %s as bool: %w", val, err) + } + case "component-name": + componentName = val + case "service-name": + serviceName = val + case "service-version": + serviceVersion = val + case "batch-timeout": + intVal, err := strconv.Atoi(val) + if err != nil { + return Options{}, errors.New("failed to parse batch-timeout as int") + } + batchTimeout = time.Duration(intVal) * time.Millisecond + case "processor-queue-size": + intVal, err := strconv.Atoi(val) + if err != nil { + return Options{}, errors.New("failed to parse processor-queue-size as int") + } + processorQueueSize = intVal + case "batch-size": + intVal, err := strconv.Atoi(val) + if err != nil { + return Options{}, errors.New("failed to parse batch-size as int") + } + batchSize = intVal + case "export-timeout": + intVal, err := strconv.Atoi(val) + if err != nil { + return Options{}, errors.New("failed to parse export-timeout as int") + } + exportTimeout = time.Duration(intVal) * time.Millisecond + case "propagators": + for _, p := range strings.Split(val, ",") { + switch strings.ToLower(p) { + case "tracecontext": + // no-op + case "baggage": + // no-op + case "ottrace": + // no-op + case "b3": + // no-op + default: + return Options{}, fmt.Errorf("unsupported propagator %s", p) + } + } + propagators = val + case "tag": + if val != "" { + tag, tagVal, found := strings.Cut(val, "=") + if !found { + return Options{}, fmt.Errorf("missing value for tag %s", val) + } + globalTags = append(globalTags, attribute.String(tag, tagVal)) + } + } + } + + if endpoint == "" { + endpoint = defaultEndpoint + } + + if lsToken == "" { + return Options{}, errors.New("missing Lightstep access token") + } + + if lsEnvironment == "" { + lsEnvironment = defaultEnvironment + } + + if !useHttp { + useGrpc = true + } + + if componentName == "" { + componentName = defaultComponentName + } + + if serviceName == "" { + serviceName = defaultServiceName + } + + if serviceVersion == "" { + serviceVersion = defaultServiceVersion + } + + if propagators == "" { + propagators = defaultPropagators + } + + var textMapPropagator []propagation.TextMapPropagator + + for _, prop := range strings.Split(propagators, ",") { + switch strings.ToLower(prop) { + case "tracecontext": + textMapPropagator = append(textMapPropagator, propagation.TraceContext{}) + case "baggage": + textMapPropagator = append(textMapPropagator, propagation.Baggage{}) + case "ottrace": + textMapPropagator = append(textMapPropagator, ottrace.OT{}) + case "b3": + textMapPropagator = append(textMapPropagator, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader|b3.B3SingleHeader))) + default: + return Options{}, fmt.Errorf("unsupported propagator %s", prop) + } + } + log.Infof("serviceName: %s, serviceVersion: %s, endpoint: %s, lsEnvironment: %s, componentName: %s, useHttp: %t, useGrpc: %t, usePlainText: %t, batchTimeout: %s, processorQueueSize: %d, batchSize: %d, exportTimeout: %s, hostname: %s, propagators: %s, globalTags: %v", serviceName, serviceVersion, endpoint, lsEnvironment, componentName, useHttp, useGrpc, usePlainText, batchTimeout, processorQueueSize, batchSize, exportTimeout, hostname, propagators, globalTags) + return Options{ + Collector: endpoint, + AccessToken: lsToken, + Environment: lsEnvironment, + UseHttp: useHttp, + UseGrpc: useGrpc, + UsePlainText: usePlainText, + ComponentName: componentName, + ServiceName: serviceName, + ServiceVersion: serviceVersion, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + Propagators: textMapPropagator, + GlobalAttributes: globalTags, + }, nil +} + +// setupOTelSDK bootstraps the OpenTelemetry pipeline. +// If it does not return an error, make sure to call shutdown for proper cleanup. +func setupOTelSDK(ctx context.Context, schemaUrl string, opts Options) (shutdown func(context.Context) error, err error) { + var shutdownFuncs []func(context.Context) error + + // shutdown calls cleanup functions registered via shutdownFuncs. + // The errors from the calls are joined. + // Each registered cleanup will be invoked once. + shutdown = func(ctx context.Context) error { + var err error + for _, fn := range shutdownFuncs { + err = errors.Join(err, fn(ctx)) + } + shutdownFuncs = nil + return err + } + + // handleErr calls shutdown for cleanup and makes sure that all errors are returned. + handleErr := func(inErr error) { + err = errors.Join(inErr, shutdown(ctx)) + } + + // Set up propagator. + otel.SetTextMapPropagator( + propagation.NewCompositeTextMapPropagator( + opts.Propagators..., + ), + ) + + // Set up trace provider. + exp, _, err := newExporter(ctx, opts) + if err != nil { + handleErr(err) + return + } + + tracerProvider, err := newTraceProvider(exp, schemaUrl, resource.Default(), opts) + if err != nil { + handleErr(err) + return + } + + shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) + otel.SetTracerProvider(tracerProvider) + + return +} + +func newExporter(ctx context.Context, opt Options) (*otlptrace.Exporter, bool, error) { + + var headers = map[string]string{ + "lightstep-access-token": opt.AccessToken, + } + + var client otlptrace.Client + var isSecure = false + + if opt.UseHttp { + var tOpt []otlptracehttp.Option + tOpt = append(tOpt, otlptracehttp.WithHeaders(headers)) + tOpt = append(tOpt, otlptracehttp.WithEndpoint(opt.Collector)) + tOpt = append(tOpt, otlptracehttp.WithURLPath(urlPath)) + if opt.UsePlainText { + tOpt = append(tOpt, otlptracehttp.WithInsecure()) + isSecure = true + } + client = otlptracehttp.NewClient(tOpt...) + } else { + var tOpt []otlptracegrpc.Option + tOpt = append(tOpt, otlptracegrpc.WithHeaders(headers)) + tOpt = append(tOpt, otlptracegrpc.WithEndpoint(opt.Collector)) + if opt.UsePlainText { + tOpt = append(tOpt, otlptracegrpc.WithInsecure()) + isSecure = true + } + client = otlptracegrpc.NewClient(tOpt...) + } + + exp, err := otlptrace.New(ctx, client) + return exp, isSecure, err +} + +func newTraceProvider(exp *otlptrace.Exporter, schemaUrl string, r *resource.Resource, opt Options) (*sdktrace.TracerProvider, error) { + + opts := opt.GlobalAttributes + + opts = append( + opts, + semconv.ServiceName(opt.ServiceName), + semconv.HostName(opt.Hostname), + semconv.ServiceVersionKey.String(opt.ServiceVersion), + attribute.String(lsEnvironmentKey, opt.Environment), + attribute.String(hostNameKey, opt.Hostname), + ) + + r, err := resource.Merge( + r, + resource.NewWithAttributes( + schemaUrl, + opts..., + ), + ) + + if err != nil { + return nil, err + } + + return sdktrace.NewTracerProvider( + sdktrace.WithBatcher( + exp, + sdktrace.WithBatchTimeout(opt.BatchTimeout), + sdktrace.WithMaxExportBatchSize(opt.BatchSize), + sdktrace.WithMaxQueueSize(opt.ProcessorQueueSize), + sdktrace.WithExportTimeout(opt.ExportTimeout), + ), + sdktrace.WithResource(r), + ), nil +} + +func InitTracer(opts []string) opentracing.Tracer { + + options, err := parseOptions(opts) + if err != nil { + log.WithError(err).Error("failed to parse options") + return &opentracing.NoopTracer{} + } + + _, err = setupOTelSDK(context.Background(), semconv.SchemaURL, options) + if err != nil { + log.WithError(err).Error("failed to set up OpenTelemetry SDK") + return &opentracing.NoopTracer{} + } + + provider := otel.GetTracerProvider() + otelTracer := provider.Tracer(defaultTracerName) + bridgeTracer, wrapperTracerProvider := otelBridge.NewTracerPair(otelTracer) + otel.SetTracerProvider(wrapperTracerProvider) + + log.Infof("OpenTelemetry Lightstep bridge tracer initialized") + + return bridgeTracer +} diff --git a/tracing/tracers/lightstepotelbridge/bridge_test.go b/tracing/tracers/lightstepotelbridge/bridge_test.go new file mode 100644 index 0000000000..61e1a04321 --- /dev/null +++ b/tracing/tracers/lightstepotelbridge/bridge_test.go @@ -0,0 +1,818 @@ +package lightstepotelbridge + +import ( + "context" + "github.com/google/go-cmp/cmp" + "github.com/opentracing/opentracing-go" + "go.opentelemetry.io/contrib/propagators/b3" + "go.opentelemetry.io/contrib/propagators/ot" + "go.opentelemetry.io/otel/attribute" + opentracing2 "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" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "os" + "reflect" + "testing" + "time" + "unsafe" +) + +func Test_parseOptions(t *testing.T) { + + var ( + batchTimeout = defaultBatchTimeout + processorQueueSize = defaultProcessorQueueSize + batchSize = defaultBatchSize + exportTimeout = defaultExportTimeout + hostname, _ = os.Hostname() + ) + + token := "mytoken" + + tests := []struct { + name string + opts []string + want Options + wantErr bool + }{ + { + name: "test without token should fail", + opts: []string{}, + want: Options{}, + wantErr: true, + }, + { + name: "test with token works", + opts: []string{"access-token=" + token}, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token works and environment set", + opts: []string{"access-token=" + token, "environment=production"}, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: "production", + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token works and set service name", + opts: []string{"access-token=" + token, "service-name=myservice"}, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: "myservice", + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token works and set service version", + opts: []string{"access-token=" + token, "service-version=1.3.4"}, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: "1.3.4", + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token works and component set", + opts: []string{"access-token=" + token, "component-name=mycomponent"}, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: "mycomponent", + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token set collector", + opts: []string{ + "access-token=" + token, + "collector=collector.example.com:8888", + }, + want: Options{ + AccessToken: token, + Collector: "collector.example.com:8888", + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token set collector wrong format", + opts: []string{ + "access-token=" + token, + "collector=collector.example.com=8888", + }, + want: Options{}, + wantErr: true, + }, + { + name: "test with token set collector wrong port", + opts: []string{ + "access-token=" + token, + "collector=collector.example.com:abc", + }, + want: Options{}, + wantErr: true, + }, + { + name: "test with token set component name", + opts: []string{ + "access-token=" + token, + "component-name=skipper-ingress", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: "skipper-ingress", + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + wantErr: false, + }, + { + name: "test with token set protocol to use grpc", + opts: []string{ + "access-token=" + token, + "protocol=grpc", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set protocol to use http", + opts: []string{ + "access-token=" + token, + "protocol=http", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseHttp: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set and wrong protocol", + opts: []string{ + "access-token=" + token, + "protocol=wrong", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token set protocol to use grpc and insecure", + opts: []string{ + "access-token=" + token, + "protocol=grpc", + "insecure-connection=true", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + UsePlainText: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set protocol to use grpc and insecure incorrect value", + opts: []string{ + "access-token=" + token, + "protocol=grpc", + "insecure-connection=wrong", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token set protocol to use http and insecure", + opts: []string{ + "access-token=" + token, + "protocol=http", + "insecure-connection=true", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseHttp: true, + UsePlainText: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set and max queue size set", + opts: []string{ + "access-token=" + token, + "processor-queue-size=100", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + ProcessorQueueSize: 100, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set and max queue size set to non numeric", + opts: []string{ + "access-token=" + token, + "processor-queue-size=wrong", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token set and batch size set", + opts: []string{ + "access-token=" + token, + "batch-size=100", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + BatchSize: 100, + ExportTimeout: exportTimeout, + Hostname: hostname, + ProcessorQueueSize: processorQueueSize, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set and batch size set to non numeric", + opts: []string{ + "access-token=" + token, + "batch-size=wrong", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token set and export timeout set", + opts: []string{ + "access-token=" + token, + "export-timeout=100", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + BatchSize: batchSize, + ExportTimeout: 100 * time.Millisecond, + Hostname: hostname, + ProcessorQueueSize: processorQueueSize, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set and export timeout set to non numeric", + opts: []string{ + "access-token=" + token, + "export-timeout=wrong", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token set and batch timeout set", + opts: []string{ + "access-token=" + token, + "batch-timeout=100", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: 100 * time.Millisecond, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + ProcessorQueueSize: processorQueueSize, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + }, + }, + { + name: "test with token set and batch timeout set to non numeric", + opts: []string{ + "access-token=" + token, + "batch-timeout=wrong", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token set and propagators set", + opts: []string{ + "access-token=" + token, + "propagators=ottrace,baggage,b3,tracecontext", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + ProcessorQueueSize: processorQueueSize, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, propagation.Baggage{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader)), propagation.TraceContext{}}, + }, + }, + { + name: "test with token set and propagators set and b3 removed", + opts: []string{ + "access-token=" + token, + "propagators=ottrace,baggage,tracecontext", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + ProcessorQueueSize: processorQueueSize, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, propagation.Baggage{}, propagation.TraceContext{}}, + }, + }, + { + name: "test with token set and propagators set and b3 removed", + opts: []string{ + "access-token=" + token, + "propagators=wro,ng", + }, + wantErr: true, + want: Options{}, + }, + { + name: "test with token works with global tag", + opts: []string{ + "access-token=" + token, + "tag=foo=bar", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + GlobalAttributes: []attribute.KeyValue{attribute.String("foo", "bar")}, + }, + wantErr: false, + }, + { + name: "test with token works with multiple global tag", + opts: []string{ + "access-token=" + token, + "tag=foo=bar", + "tag=bar=foo", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + GlobalAttributes: []attribute.KeyValue{attribute.String("foo", "bar"), attribute.String("bar", "foo")}, + }, + wantErr: false, + }, + { + name: "test with token works with global tag empty", + opts: []string{ + "access-token=" + token, + "tag=", + }, + want: Options{ + AccessToken: token, + Collector: defaultEndpoint, + Environment: defaultEnvironment, + ServiceName: defaultServiceName, + ServiceVersion: defaultServiceVersion, + ComponentName: defaultComponentName, + BatchTimeout: batchTimeout, + ProcessorQueueSize: processorQueueSize, + BatchSize: batchSize, + ExportTimeout: exportTimeout, + Hostname: hostname, + UseGrpc: true, + Propagators: []propagation.TextMapPropagator{ot.OT{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader))}, + //GlobalAttributes: []attribute.KeyValue{attribute.String("foo", "bar")}, + }, + wantErr: false, + }, + { + name: "test with token works with global tag wrong format", + opts: []string{ + "access-token=" + token, + "tag=wrong", + }, + wantErr: true, + want: Options{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseOptions(tt.opts) + if (err != nil) != tt.wantErr { + t.Errorf("parseOptions() error = %v, wantErr %v", err, tt.wantErr) + return + } + //if !reflect.DeepEqual(got.Propagators, tt.propagators) { + // t.Logf("diff: %v", cmp.Diff(tt.propagators, got.Propagators)) + // t.Errorf("propagators = %v, want %v", got.Propagators, tt.propagators) + //} + //got.Propagators = nil + + if !reflect.DeepEqual(got, tt.want) { + t.Logf("diff: %v", cmp.Diff(tt.want, got)) + t.Errorf("parseOptions() = %v, want %v", got, tt.want) + } + }) + } + +} + +func Test_newExporter(t *testing.T) { + type args struct { + ctx context.Context + opt Options + } + tests := []struct { + name string + args args + wantErr bool + want interface{} + wantPlainText bool + }{ + { + name: "test with grpc", + args: args{ + ctx: context.Background(), + opt: Options{}, + }, + wantErr: false, + want: otlptracegrpc.NewClient(), + }, + { + name: "test with grpc and plain text", + args: args{ + ctx: context.Background(), + opt: Options{ + UsePlainText: true, + }, + }, + wantErr: false, + want: otlptracegrpc.NewClient(), + wantPlainText: true, + }, + { + name: "test with http", + args: args{ + ctx: context.Background(), + opt: Options{ + UseHttp: true, + }, + }, + wantErr: false, + want: otlptracehttp.NewClient(), + }, + { + name: "test with http and plain text", + args: args{ + ctx: context.Background(), + opt: Options{ + UseHttp: true, + UsePlainText: true, + }, + }, + wantErr: false, + want: otlptracehttp.NewClient(), + wantPlainText: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, plainText, err := newExporter(tt.args.ctx, tt.args.opt) + if (err != nil) != tt.wantErr { + t.Errorf("newExporter() error = %v, wantErr %v", err, tt.wantErr) + return + } + + gotClient := reflect.ValueOf(got).Elem().FieldByName("client") + // Use unsafe to get the underlying value of the unexported field + ptr := unsafe.Pointer(gotClient.UnsafeAddr()) + concreteValue := reflect.NewAt(gotClient.Type(), ptr).Elem().Interface() + concreteType := reflect.TypeOf(concreteValue) + + if plainText != tt.wantPlainText { + t.Errorf("newExporter() = %v, want %v", plainText, tt.wantPlainText) + } + + if !reflect.DeepEqual(concreteType, reflect.TypeOf(tt.want)) { + t.Errorf("newExporter() = %v, want %v", concreteType, reflect.TypeOf(tt.want)) + } + }) + } +} + +func Test_newTraceProvider(t *testing.T) { + + exp := &otlptrace.Exporter{} + opts := Options{} + r := resource.Default() + tp := sdktrace.NewTracerProvider() + + type args struct { + exp *otlptrace.Exporter + schemaUrl string + r *resource.Resource + opt Options + } + tests := []struct { + name string + args args + want interface{} + wantErr bool + }{ + { + name: "test newTraceProvider fail resource.Merge", + args: args{ + exp: exp, + schemaUrl: "wrongschema", + opt: opts, + r: r, + }, + wantErr: true, + }, + { + name: "test newTraceProvider success", + args: args{ + exp: exp, + schemaUrl: semconv.SchemaURL, + opt: opts, + r: r, + }, + wantErr: false, + want: tp, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newTraceProvider(tt.args.exp, tt.args.schemaUrl, tt.args.r, tt.args.opt) + if err != nil { + if (err != nil) != tt.wantErr { + t.Errorf("newTraceProvider() error = %v, wantErr %v", err, tt.wantErr) + } + return + } + + if reflect.TypeOf(got) != reflect.TypeOf(tt.want) { + t.Errorf("newTraceProvider() got = %v, want %v", reflect.TypeOf(got), reflect.TypeOf(tt.want)) + } + }) + } +} + +func Test_setupOTelSDK(t *testing.T) { + type args struct { + ctx context.Context + schemaUrl string + opts Options + } + tests := []struct { + name string + args args + wantShutdown func(context.Context) error + wantErr bool + }{ + { + name: "test setupOTelSDK success", + }, + { + name: "test setupOTelSDK fail", + args: args{ + ctx: context.Background(), + opts: Options{}, + schemaUrl: "wrong", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotShutdown, err := setupOTelSDK(tt.args.ctx, tt.args.schemaUrl, tt.args.opts) + if (err != nil) != tt.wantErr { + t.Errorf("setupOTelSDK() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if reflect.TypeOf(gotShutdown) != reflect.TypeOf(tt.wantShutdown) { + t.Errorf("setupOTelSDK() gotShutdown = %v, want %v", reflect.TypeOf(gotShutdown), reflect.TypeOf(tt.wantShutdown)) + } + }) + } +} + +func TestInitTracer(t *testing.T) { + + type args struct { + opts []string + } + tests := []struct { + name string + args args + want interface{} + }{ + { + name: "test InitTracer successful", + want: &opentracing2.BridgeTracer{}, + args: args{ + opts: []string{"access-token=mytoken", "collector=example.com:8443"}, + }, + }, + { + name: "test InitTracer fail parse options and return no op tracer", + want: &opentracing.NoopTracer{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := InitTracer(tt.args.opts) + if reflect.TypeOf(got) != reflect.TypeOf(tt.want) { + t.Errorf("InitTracer() got = %v, want %v", reflect.TypeOf(got), reflect.TypeOf(tt.want)) + } + }) + } +} diff --git a/tracing/tracers/otel/otel.go b/tracing/tracers/otel/otel.go deleted file mode 100644 index 44b1374991..0000000000 --- a/tracing/tracers/otel/otel.go +++ /dev/null @@ -1,311 +0,0 @@ -package otel - -import ( - "context" - "errors" - "github.com/opentracing/opentracing-go" - log "github.com/sirupsen/logrus" - "go.opentelemetry.io/contrib/propagators/b3" - ottrace "go.opentelemetry.io/contrib/propagators/ot" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - otelBridge "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" - "go.opentelemetry.io/otel/sdk/resource" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.26.0" - "os" - "strconv" - "strings" - "time" -) - -const ( - HostNameKey = "hostname" - LSHostNameKey = "lightstep.hostname" - LSEnvironmentKey = "environment" - LSComponentNameKey = "lightstep.component_name" - DefaultEndpoint = "ingest.lightstep.com:443" - DefaultServiceName = "skipper" - DefaultServiceVersion = "0.1.0" - DefaultEnvironment = "dev" - DefaultTracerName = "otel-lightstep-bridge" - DefaultComponentName = "skipper" -) - -var ( - serviceName = os.Getenv("LS_SERVICE_NAME") - urlPath = "traces/otlp/v0.9" - serviceVersion = os.Getenv("LS_SERVICE_VERSION") - endpoint = os.Getenv("LS_SATELLITE_URL") - lsToken = os.Getenv("LS_ACCESS_TOKEN") - lsEnvironment = os.Getenv("LS_ENVIRONMENT") - hostname, _ = os.Hostname() - batchTimeout = 2500 * time.Millisecond - processorQueueSize = 10000 - batchSize = 512 - exportTimeout = 5000 * time.Millisecond -) - -type Options struct { - Collector string - AccessToken string - Environment string - UseHttp bool - UseGrpc bool - UsePlainText bool - ComponentName string - ServiceName string - ServiceVersion string -} - -func parseOptions(opts []string) (Options, error) { - var ( - collector, accessToken, environment, componentName, serviceName, serviceVersion string - useHttp, useGrpc, usePlainText bool - ) - - for _, o := range opts { - key, val, _ := strings.Cut(o, "=") - switch key { - case "collector": - collector = val - case "access-token": - accessToken = val - case "environment": - environment = val - case "use-http": - useHttp = true - case "use-grpc": - useGrpc = true - case "use-plain-text": - usePlainText = true - case "component-name": - componentName = val - case "service-name": - serviceName = val - case "service-version": - serviceVersion = val - case "batch-timeout": - intVal, err := strconv.Atoi(val) - if err != nil { - return Options{}, errors.New("failed to parse batch-timeout as int") - } - batchTimeout = time.Duration(intVal) * time.Millisecond - case "processor-queue-size": - intVal, err := strconv.Atoi(val) - if err != nil { - return Options{}, errors.New("failed to parse processor-queue-size as int") - } - processorQueueSize = intVal - case "batch-size": - intVal, err := strconv.Atoi(val) - if err != nil { - return Options{}, errors.New("failed to parse batch-size as int") - } - batchSize = intVal - case "export-timeout": - intVal, err := strconv.Atoi(val) - if err != nil { - return Options{}, errors.New("failed to parse export-timeout as int") - } - exportTimeout = time.Duration(intVal) * time.Millisecond - } - } - - return Options{ - Collector: collector, - AccessToken: accessToken, - Environment: environment, - UseHttp: useHttp, - UseGrpc: useGrpc, - UsePlainText: usePlainText, - ComponentName: componentName, - ServiceName: serviceName, - ServiceVersion: serviceVersion, - }, nil -} - -// setupOTelSDK bootstraps the OpenTelemetry pipeline. -// If it does not return an error, make sure to call shutdown for proper cleanup. -func setupOTelSDK(ctx context.Context, opts Options) (shutdown func(context.Context) error, err error) { - var shutdownFuncs []func(context.Context) error - - // shutdown calls cleanup functions registered via shutdownFuncs. - // The errors from the calls are joined. - // Each registered cleanup will be invoked once. - shutdown = func(ctx context.Context) error { - var err error - for _, fn := range shutdownFuncs { - err = errors.Join(err, fn(ctx)) - } - shutdownFuncs = nil - return err - } - - // handleErr calls shutdown for cleanup and makes sure that all errors are returned. - handleErr := func(inErr error) { - err = errors.Join(inErr, shutdown(ctx)) - } - - // Set up propagator. - otel.SetTextMapPropagator( - propagation.NewCompositeTextMapPropagator( - //propagation.TraceContext{}, - //propagation.Baggage{}, - ottrace.OT{}, - b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader|b3.B3SingleHeader)), - ), - ) - - // Set up trace provider. - exp, err := newExporter(ctx, opts) - if err != nil { - handleErr(err) - return - } - - tracerProvider, err := newTraceProvider(exp, opts) - if err != nil { - handleErr(err) - return - } - - shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) - otel.SetTracerProvider(tracerProvider) - - return -} - -func newExporter(ctx context.Context, opt Options) (*otlptrace.Exporter, error) { - - if len(opt.Collector) > 0 { - endpoint = opt.Collector - log.Infof("Using custom LS endpoint %s/%s", endpoint, urlPath) - } else if len(endpoint) == 0 { - endpoint = DefaultEndpoint - log.Infof("Using default LS endpoint %s/%s", endpoint, urlPath) - } - - if len(opt.AccessToken) > 0 { - lsToken = opt.AccessToken - log.Infof("Using custom LS token") - } - - if len(lsToken) == 0 { - return nil, errors.New("missing Lightstep access token") - } - - var headers = map[string]string{ - "lightstep-access-token": lsToken, - } - - var client otlptrace.Client - - if opt.UseGrpc { - var tOpt []otlptracegrpc.Option - tOpt = append(tOpt, otlptracegrpc.WithHeaders(headers)) - tOpt = append(tOpt, otlptracegrpc.WithEndpoint(endpoint)) - if opt.UsePlainText { - tOpt = append(tOpt, otlptracegrpc.WithInsecure()) - } - client = otlptracegrpc.NewClient(tOpt...) - } else { - var tOpt []otlptracehttp.Option - tOpt = append(tOpt, otlptracehttp.WithHeaders(headers)) - tOpt = append(tOpt, otlptracehttp.WithEndpoint(endpoint)) - tOpt = append(tOpt, otlptracehttp.WithURLPath(urlPath)) - if opt.UsePlainText { - tOpt = append(tOpt, otlptracehttp.WithInsecure()) - } - client = otlptracehttp.NewClient(tOpt...) - } - - return otlptrace.New(ctx, client) -} - -func newTraceProvider(exp *otlptrace.Exporter, opt Options) (*sdktrace.TracerProvider, error) { - - var componentName = DefaultComponentName - if len(opt.ComponentName) > 0 { - componentName = opt.ComponentName - log.Infof("Using custom component name %s", componentName) - } - - if len(opt.ServiceName) > 0 { - serviceName = opt.ServiceName - log.Infof("Using custom service name %s", serviceName) - } else if len(serviceName) == 0 { - serviceName = DefaultServiceName - log.Infof("Using default service name %s", serviceName) - } - - if len(opt.ServiceVersion) > 0 { - serviceVersion = opt.ServiceVersion - log.Infof("Using custom service version %s", serviceVersion) - } else if len(serviceVersion) == 0 { - serviceVersion = DefaultServiceVersion - log.Infof("Using default service version %s", serviceVersion) - } - - if len(opt.Environment) > 0 { - lsEnvironment = opt.Environment - log.Infof("Using custom environment %s", lsEnvironment) - } else if len(lsEnvironment) == 0 { - lsEnvironment = DefaultEnvironment - log.Infof("Using default environment %s", lsEnvironment) - } - - r, err := - resource.Merge( - resource.Default(), - resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceNameKey.String(serviceName), - semconv.ServiceVersionKey.String(serviceVersion), - attribute.String(LSEnvironmentKey, lsEnvironment), - attribute.String(LSComponentNameKey, componentName), - // TODO does not work - attribute.String(LSHostNameKey, hostname), - attribute.String(HostNameKey, hostname), - ), - ) - - if err != nil { - return nil, err - } - - return sdktrace.NewTracerProvider( - sdktrace.WithBatcher( - exp, - sdktrace.WithBatchTimeout(batchTimeout), - sdktrace.WithMaxExportBatchSize(batchSize), - sdktrace.WithMaxQueueSize(processorQueueSize), - sdktrace.WithExportTimeout(exportTimeout), - ), - sdktrace.WithResource(r), - ), nil -} - -func InitTracer(opts []string) opentracing.Tracer { - - options, err := parseOptions(opts) - - _, err = setupOTelSDK(context.Background(), options) - if err != nil { - log.WithError(err).Error("failed to set up OpenTelemetry SDK") - return nil - } - - provider := otel.GetTracerProvider() - otelTracer := provider.Tracer(DefaultTracerName) - bridgeTracer, wrapperTracerProvider := otelBridge.NewTracerPair(otelTracer) - otel.SetTracerProvider(wrapperTracerProvider) - - log.Infof("OpenTelemetry Lightstep bridge tracer initialized") - - return bridgeTracer -} diff --git a/tracing/tracing.go b/tracing/tracing.go index 23b7655188..4c13628c94 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -53,7 +53,7 @@ import ( "context" "errors" "fmt" - "github.com/zalando/skipper/tracing/tracers/otel" + "github.com/zalando/skipper/tracing/tracers/lightstepotelbridge" "path/filepath" "plugin" @@ -87,8 +87,8 @@ func InitTracer(opts []string) (tracer ot.Tracer, err error) { return instana.InitTracer(opts) case "jaeger": return jaeger.InitTracer(opts) - case "otel": - return otel.InitTracer(opts), nil + case "lightstep-otel-bridge": + return lightstepotelbridge.InitTracer(opts), nil case "lightstep": return lightstep.InitTracer(opts) default: