diff --git a/options.go b/options.go index aca6f2fd9..c41b03964 100644 --- a/options.go +++ b/options.go @@ -170,8 +170,13 @@ type Options struct { UseHttp bool `yaml:"use_http"` UseGRPC bool `yaml:"usegrpc"` - // Propagator provides support for different types of headers. Supported options: ls, b3 - Propagator string `yaml:"propagator"` + // Propagators allow inject/extract to use custom propagators for different formats. This + // package includes a `B3Propagator` that supports B3 headers on text maps and http headers. + // Defaults: + // opentracing.HTTPHeaders: LightStepPropagator + // opentracing.TextMap: LightStepPropagator, + // opentracing.Binary: LightStepPropagator + Propagators map[opentracing.BuiltinFormat]Propagator `yaml:"-"` // CustomCollector allows customizing the Protobuf transport. // This is an advanced feature that avoids reconnect logic. diff --git a/propagation_b3.go b/propagation_b3.go index 9bfb4a847..ce695a525 100644 --- a/propagation_b3.go +++ b/propagation_b3.go @@ -14,7 +14,8 @@ const ( b3FieldNameSampled = b3Prefix + "sampled" ) -var theB3Propagator b3Propagator +// B3Propagator propagates context in the b3 format +var B3Propagator b3Propagator type b3Propagator struct{} diff --git a/propagation_binary.go b/propagation_binary.go index 057666b94..cd4a6f79d 100644 --- a/propagation_binary.go +++ b/propagation_binary.go @@ -13,7 +13,8 @@ import ( // BinaryCarrier is used as the format parameter in inject/extract for lighstep binary propagation. const BinaryCarrier = opentracing.Binary -var theBinaryPropagator binaryPropagator +// BinaryPropagator propagates context in binary format +var BinaryPropagator binaryPropagator type binaryPropagator struct{} diff --git a/propagation_lightstep.go b/propagation_lightstep.go index 7a770e9f8..47fdc3681 100644 --- a/propagation_lightstep.go +++ b/propagation_lightstep.go @@ -13,7 +13,8 @@ const ( fieldNameSampled = prefixTracerState + "sampled" ) -var theLightStepPropagator lightstepPropagator +// LightStepPropagator propagates context in the LightStep format +var LightStepPropagator lightstepPropagator type lightstepPropagator struct{} diff --git a/propagation_stack.go b/propagation_stack.go new file mode 100644 index 000000000..c36749cb4 --- /dev/null +++ b/propagation_stack.go @@ -0,0 +1,52 @@ +package lightstep + +import ( + "errors" + + "github.com/opentracing/opentracing-go" +) + +// PropagatorStack provides a Propagator interface that supports +// multiple propagators per format. +type PropagatorStack struct { + propagators []Propagator +} + +// PushPropagator adds a Propagator to a list of configured propagators +func (stack *PropagatorStack) PushPropagator(p Propagator) { + stack.propagators = append(stack.propagators, p) +} + +// Inject iterates through configured propagators and calls +// their Inject functions +func (stack PropagatorStack) Inject( + spanContext opentracing.SpanContext, + opaqueCarrier interface{}, +) error { + if len(stack.propagators) == 0 { + return errors.New("No valid propagator configured") + } + for _, propagator := range stack.propagators { + propagator.Inject(spanContext, opaqueCarrier) + } + return nil +} + +// Extract iterates through configured propagators and +// returns the first successfully extracted context +func (stack PropagatorStack) Extract( + opaqueCarrier interface{}, +) (opentracing.SpanContext, error) { + if len(stack.propagators) == 0 { + return nil, errors.New("No valid propagator configured") + } + + for _, propagator := range stack.propagators { + context, err := propagator.Extract(opaqueCarrier) + if err == nil { + return context, nil + } + } + + return nil, errors.New("No valid propagator configured") +} diff --git a/propagation_stack_test.go b/propagation_stack_test.go new file mode 100644 index 000000000..242518edc --- /dev/null +++ b/propagation_stack_test.go @@ -0,0 +1,134 @@ +package lightstep_test + +import ( + . "github.com/lightstep/lightstep-tracer-go" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/opentracing/opentracing-go" +) + +var _ = Describe("Propagator Stack", func() { + var stack PropagatorStack + Context("With no propagators", func() { + var knownCarrier1 opentracing.TextMapCarrier + + var knownContext1 = SpanContext{ + SpanID: 6397081719746291766, + TraceID: 506100417967962170, + Baggage: map[string]string{"checked": "baggage"}, + } + BeforeEach(func() { + stack = PropagatorStack{} + }) + + It("should return error on inject", func() { + err := stack.Inject(knownContext1, knownCarrier1) + Expect(err).ToNot(BeNil()) + }) + + It("should return error on extract", func() { + _, err := stack.Extract(knownCarrier1) + Expect(err).ToNot(BeNil()) + }) + }) + Context("With one propagator", func() { + var knownCarrier1 opentracing.TextMapCarrier + + var knownContext1 = SpanContext{ + SpanID: 6397081719746291766, + TraceID: 506100417967962170, + Baggage: map[string]string{"checked": "baggage"}, + } + BeforeEach(func() { + knownCarrier1 = opentracing.TextMapCarrier{} + stack = PropagatorStack{} + stack.PushPropagator(LightStepPropagator) + }) + + It("should inject trace", func() { + err := stack.Inject(knownContext1, knownCarrier1) + Expect(err).To(BeNil()) + Expect(len(knownCarrier1)).To(Equal(4)) + Expect(knownCarrier1["ot-tracer-traceid"]).To(Equal("70607a611a8383a")) + Expect(knownCarrier1["ot-tracer-spanid"]).To(Equal("58c6ffee509f6836")) + Expect(knownCarrier1["ot-tracer-sampled"]).To(Equal("true")) + Expect(knownCarrier1["ot-baggage-checked"]).To(Equal("baggage")) + }) + + It("should extract trace", func() { + knownCarrier2 := opentracing.TextMapCarrier{ + "ot-tracer-traceid": "70607a611a8383a", + "ot-tracer-spanid": "58c6ffee509f6836", + "ot-tracer-sampled": "true", + "ot-baggage-checked": "baggage", + } + ctx, err := stack.Extract(knownCarrier2) + Expect(err).To(BeNil()) + // check if spancontext is correct + spanContext, _ := ctx.(SpanContext) + + Expect(spanContext.TraceID).To(Equal(knownContext1.TraceID)) + Expect(spanContext.SpanID).To(Equal(knownContext1.SpanID)) + Expect(spanContext.Baggage).To(Equal(knownContext1.Baggage)) + }) + }) + Context("With multiple propagator", func() { + var knownCarrier1 opentracing.TextMapCarrier + + var knownContext1 = SpanContext{ + SpanID: 6397081719746291766, + TraceID: 506100417967962170, + Baggage: map[string]string{"checked": "baggage"}, + } + BeforeEach(func() { + knownCarrier1 = opentracing.TextMapCarrier{} + stack = PropagatorStack{} + stack.PushPropagator(LightStepPropagator) + stack.PushPropagator(B3Propagator) + }) + + It("should inject trace using both propagators", func() { + err := stack.Inject(knownContext1, knownCarrier1) + Expect(err).To(BeNil()) + Expect(len(knownCarrier1)).To(Equal(7)) + + Expect(knownCarrier1["ot-tracer-traceid"]).To(Equal("70607a611a8383a")) + Expect(knownCarrier1["ot-tracer-spanid"]).To(Equal("58c6ffee509f6836")) + Expect(knownCarrier1["ot-tracer-sampled"]).To(Equal("true")) + Expect(knownCarrier1["x-b3-traceid"]).To(Equal("70607a611a8383a")) + Expect(knownCarrier1["x-b3-spanid"]).To(Equal("58c6ffee509f6836")) + Expect(knownCarrier1["x-b3-sampled"]).To(Equal("1")) + Expect(knownCarrier1["ot-baggage-checked"]).To(Equal("baggage")) + }) + + It("should extract trace", func() { + knownCarrier2 := opentracing.TextMapCarrier{ + "ot-tracer-traceid": "70607a611a8383a", + "ot-tracer-spanid": "58c6ffee509f6836", + "ot-tracer-sampled": "true", + "ot-baggage-checked": "baggage", + } + ctx, err := stack.Extract(knownCarrier2) + Expect(err).To(BeNil()) + spanContext, _ := ctx.(SpanContext) + + Expect(spanContext.TraceID).To(Equal(knownContext1.TraceID)) + Expect(spanContext.SpanID).To(Equal(knownContext1.SpanID)) + Expect(spanContext.Baggage).To(Equal(knownContext1.Baggage)) + + knownCarrier3 := opentracing.TextMapCarrier{ + "x-b3-traceid": "70607a611a8383a", + "x-b3-spanid": "58c6ffee509f6836", + "x-b3-sampled": "true", + "ot-baggage-checked": "baggage", + } + ctx, err = stack.Extract(knownCarrier3) + Expect(err).To(BeNil()) + spanContext, _ = ctx.(SpanContext) + + Expect(spanContext.TraceID).To(Equal(knownContext1.TraceID)) + Expect(spanContext.SpanID).To(Equal(knownContext1.SpanID)) + Expect(spanContext.Baggage).To(Equal(knownContext1.Baggage)) + }) + }) +}) diff --git a/tracer.go b/tracer.go index e2678681b..b4119edfd 100644 --- a/tracer.go +++ b/tracer.go @@ -93,7 +93,7 @@ type tracerImpl struct { // In case of error, we emit event and return nil. func NewTracer(opts Options) Tracer { tr, err := CreateTracer(opts) - if err != nil { + if err != nil { emitEvent(newEventStartError(err)) return nil } @@ -150,14 +150,12 @@ func CreateTracer(opts Options) (Tracer, error) { go impl.reportLoop() impl.propagators = map[opentracing.BuiltinFormat]Propagator{ - opentracing.TextMap: theLightStepPropagator, - opentracing.HTTPHeaders: theLightStepPropagator, - opentracing.Binary: theBinaryPropagator, + opentracing.TextMap: LightStepPropagator, + opentracing.HTTPHeaders: LightStepPropagator, + opentracing.Binary: BinaryPropagator, } - - if opts.Propagator == "b3" { - impl.propagators[opentracing.TextMap] = theB3Propagator - impl.propagators[opentracing.HTTPHeaders] = theB3Propagator + for builtin, propagator := range opts.Propagators { + impl.propagators[builtin] = propagator } return impl, nil diff --git a/tracer_transport_test.go b/tracer_transport_test.go index a67e3b4e9..a13aba466 100644 --- a/tracer_transport_test.go +++ b/tracer_transport_test.go @@ -558,7 +558,10 @@ var _ = Describe("Tracer Transports", func() { } BeforeEach(func() { - options.Propagator = "b3" + options.Propagators = map[opentracing.BuiltinFormat]Propagator{ + opentracing.HTTPHeaders: B3Propagator, + opentracing.TextMap: B3Propagator, + } knownCarrier1 = opentracing.TextMapCarrier{} })