diff --git a/config/config.go b/config/config.go index 32aa82ce..59debe25 100644 --- a/config/config.go +++ b/config/config.go @@ -151,6 +151,10 @@ func (c Configuration) New( tracerOptions = append(tracerOptions, jaeger.TracerOptions.Observer(obs)) } + for _, tag := range opts.tags { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Tag(tag.Key, tag.Value)) + } + tracer, closer := jaeger.NewTracer( serviceName, sampler, diff --git a/config/options.go b/config/options.go index e173332a..f10fa599 100644 --- a/config/options.go +++ b/config/options.go @@ -21,6 +21,7 @@ package config import ( + opentracing "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-client-go" @@ -36,6 +37,7 @@ type Options struct { reporter jaeger.Reporter observers []jaeger.Observer zipkinSharedRPCSpan bool + tags []opentracing.Tag } // Metrics creates an Option that initializes Metrics in the tracer, @@ -78,6 +80,13 @@ func ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) Option { } } +// Tag creates an option that adds a tracer-level tag. +func Tag(key string, value interface{}) Option { + return func(c *Options) { + c.tags = append(c.tags, opentracing.Tag{Key: key, Value: value}) + } +} + func applyOptions(options ...Option) Options { opts := Options{} for _, option := range options { diff --git a/config/options_test.go b/config/options_test.go index 2dcd929a..aba4e824 100644 --- a/config/options_test.go +++ b/config/options_test.go @@ -25,6 +25,7 @@ import ( opentracing "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-client-go" @@ -45,6 +46,14 @@ func TestApplyOptions(t *testing.T) { assert.True(t, opts.zipkinSharedRPCSpan) } +func TestTraceTagOption(t *testing.T) { + c := Configuration{} + tracer, closer, err := c.New("test-service", Tag("tag-key", "tag-value")) + require.NoError(t, err) + defer closer.Close() + assert.Equal(t, opentracing.Tag{Key: "tag-key", Value: "tag-value"}, tracer.(*jaeger.Tracer).Tags()[0]) +} + func TestApplyOptionsDefaults(t *testing.T) { opts := applyOptions() assert.Equal(t, jaeger.NullLogger, opts.logger) diff --git a/interop.go b/interop.go index 556cf7cd..23becc2c 100644 --- a/interop.go +++ b/interop.go @@ -34,7 +34,7 @@ type formatKey int const SpanContextFormat formatKey = iota type jaegerTraceContextPropagator struct { - tracer *tracer + tracer *Tracer } func (p *jaegerTraceContextPropagator) Inject( diff --git a/jaeger_thrift_span.go b/jaeger_thrift_span.go index d45a315a..3335b5c8 100644 --- a/jaeger_thrift_span.go +++ b/jaeger_thrift_span.go @@ -54,7 +54,7 @@ func BuildJaegerProcessThrift(span *Span) *j.Process { return buildJaegerProcessThrift(span.tracer) } -func buildJaegerProcessThrift(tracer *tracer) *j.Process { +func buildJaegerProcessThrift(tracer *Tracer) *j.Process { process := &j.Process{ ServiceName: tracer.serviceName, Tags: buildTags(tracer.tags), diff --git a/propagation.go b/propagation.go index b64e4f8b..074d78d5 100644 --- a/propagation.go +++ b/propagation.go @@ -58,12 +58,12 @@ type Extractor interface { } type textMapPropagator struct { - tracer *tracer + tracer *Tracer encodeValue func(string) string decodeValue func(string) string } -func newTextMapPropagator(tracer *tracer) *textMapPropagator { +func newTextMapPropagator(tracer *Tracer) *textMapPropagator { return &textMapPropagator{ tracer: tracer, encodeValue: func(val string) string { @@ -75,7 +75,7 @@ func newTextMapPropagator(tracer *tracer) *textMapPropagator { } } -func newHTTPHeaderPropagator(tracer *tracer) *textMapPropagator { +func newHTTPHeaderPropagator(tracer *Tracer) *textMapPropagator { return &textMapPropagator{ tracer: tracer, encodeValue: func(val string) string { @@ -92,11 +92,11 @@ func newHTTPHeaderPropagator(tracer *tracer) *textMapPropagator { } type binaryPropagator struct { - tracer *tracer + tracer *Tracer buffers sync.Pool } -func newBinaryPropagator(tracer *tracer) *binaryPropagator { +func newBinaryPropagator(tracer *Tracer) *binaryPropagator { return &binaryPropagator{ tracer: tracer, buffers: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}, diff --git a/span.go b/span.go index 07c30d16..6c23d77f 100644 --- a/span.go +++ b/span.go @@ -34,7 +34,7 @@ import ( type Span struct { sync.RWMutex - tracer *tracer + tracer *Tracer context SpanContext @@ -66,7 +66,8 @@ type Span struct { observer SpanObserver } -// Tag is a simple key value wrapper +// Tag is a simple key value wrapper. +// TODO deprecate in the next major release, use opentracing.Tag instead. type Tag struct { key string value interface{} diff --git a/tracer.go b/tracer.go index a91bff76..8f74e9c8 100644 --- a/tracer.go +++ b/tracer.go @@ -35,7 +35,8 @@ import ( "github.com/uber/jaeger-client-go/utils" ) -type tracer struct { +// Tracer implements opentracing.Tracer. +type Tracer struct { serviceName string hostIPv4 uint32 // this is for zipkin endpoint conversion @@ -73,7 +74,7 @@ func NewTracer( reporter Reporter, options ...TracerOption, ) (opentracing.Tracer, io.Closer) { - t := &tracer{ + t := &Tracer{ serviceName: serviceName, sampler: sampler, reporter: reporter, @@ -138,7 +139,8 @@ func NewTracer( return t, t } -func (t *tracer) StartSpan( +// StartSpan implements StartSpan() method of opentracing.Tracer. +func (t *Tracer) StartSpan( operationName string, options ...opentracing.StartSpanOption, ) opentracing.Span { @@ -149,7 +151,7 @@ func (t *tracer) StartSpan( return t.startSpanWithOptions(operationName, sso) } -func (t *tracer) startSpanWithOptions( +func (t *Tracer) startSpanWithOptions( operationName string, options opentracing.StartSpanOptions, ) opentracing.Span { @@ -245,7 +247,7 @@ func (t *tracer) startSpanWithOptions( } // Inject implements Inject() method of opentracing.Tracer -func (t *tracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error { +func (t *Tracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error { c, ok := ctx.(SpanContext) if !ok { return opentracing.ErrInvalidSpanContext @@ -257,7 +259,7 @@ func (t *tracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier } // Extract implements Extract() method of opentracing.Tracer -func (t *tracer) Extract( +func (t *Tracer) Extract( format interface{}, carrier interface{}, ) (opentracing.SpanContext, error) { @@ -268,15 +270,24 @@ func (t *tracer) Extract( } // Close releases all resources used by the Tracer and flushes any remaining buffered spans. -func (t *tracer) Close() error { +func (t *Tracer) Close() error { t.reporter.Close() t.sampler.Close() return nil } +// Tags returns a slice of tracer-level tags. +func (t *Tracer) Tags() []opentracing.Tag { + tags := make([]opentracing.Tag, len(t.tags)) + for i, tag := range t.tags { + tags[i] = opentracing.Tag{Key: tag.key, Value: tag.value} + } + return tags +} + // newSpan returns an instance of a clean Span object. // If options.PoolSpans is true, the spans are retrieved from an object pool. -func (t *tracer) newSpan() *Span { +func (t *Tracer) newSpan() *Span { if !t.options.poolSpans { return &Span{} } @@ -288,7 +299,7 @@ func (t *tracer) newSpan() *Span { return sp } -func (t *tracer) startSpanInternal( +func (t *Tracer) startSpanInternal( sp *Span, operationName string, startTime time.Time, @@ -339,7 +350,7 @@ func (t *tracer) startSpanInternal( return sp } -func (t *tracer) reportSpan(sp *Span) { +func (t *Tracer) reportSpan(sp *Span) { t.metrics.SpansFinished.Inc(1) if sp.context.IsSampled() { t.reporter.Report(sp) @@ -351,7 +362,7 @@ func (t *tracer) reportSpan(sp *Span) { // randomID generates a random trace/span ID, using tracer.random() generator. // It never returns 0. -func (t *tracer) randomID() uint64 { +func (t *Tracer) randomID() uint64 { val := t.randomNumber() for val == 0 { val = t.randomNumber() diff --git a/tracer_options.go b/tracer_options.go index bb27ae5c..226db95f 100644 --- a/tracer_options.go +++ b/tracer_options.go @@ -25,7 +25,7 @@ import ( ) // TracerOption is a function that sets some option on the tracer -type TracerOption func(tracer *tracer) +type TracerOption func(tracer *Tracer) // TracerOptions is a factory for all available TracerOption's var TracerOptions tracerOptions @@ -35,14 +35,14 @@ type tracerOptions struct{} // Metrics creates a TracerOption that initializes Metrics on the tracer, // which is used to emit statistics. func (tracerOptions) Metrics(m *Metrics) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.metrics = *m } } // Logger creates a TracerOption that gives the tracer a Logger. func (tracerOptions) Logger(logger Logger) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.logger = logger } } @@ -50,7 +50,7 @@ func (tracerOptions) Logger(logger Logger) TracerOption { // TimeNow creates a TracerOption that gives the tracer a function // used to generate timestamps for spans. func (tracerOptions) TimeNow(timeNow func() time.Time) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.timeNow = timeNow } } @@ -58,7 +58,7 @@ func (tracerOptions) TimeNow(timeNow func() time.Time) TracerOption { // RandomNumber creates a TracerOption that gives the tracer // a thread-safe random number generator function for generating trace IDs. func (tracerOptions) RandomNumber(randomNumber func() uint64) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.randomNumber = randomNumber } } @@ -68,7 +68,7 @@ func (tracerOptions) RandomNumber(randomNumber func() uint64) TracerOption { // This should be used with care, only if the service is not running any async tasks // that can access parent spans after those spans have been finished. func (tracerOptions) PoolSpans(poolSpans bool) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.options.poolSpans = poolSpans } } @@ -77,31 +77,37 @@ func (tracerOptions) PoolSpans(poolSpans bool) TracerOption { // If not set, the factory method will obtain the current IP address. // The TracerOption is deprecated; the tracer will attempt to automatically detect the IP. func (tracerOptions) HostIPv4(hostIPv4 uint32) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.hostIPv4 = hostIPv4 } } func (tracerOptions) Injector(format interface{}, injector Injector) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.injectors[format] = injector } } func (tracerOptions) Extractor(format interface{}, extractor Extractor) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.extractors[format] = extractor } } func (tracerOptions) Observer(observer Observer) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.observer.append(observer) } } func (tracerOptions) ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) TracerOption { - return func(tracer *tracer) { + return func(tracer *Tracer) { tracer.options.zipkinSharedRPCSpan = zipkinSharedRPCSpan } } + +func (tracerOptions) Tag(key string, value interface{}) TracerOption { + return func(tracer *Tracer) { + tracer.tags = append(tracer.tags, Tag{key: key, value: value}) + } +} diff --git a/tracer_test.go b/tracer_test.go index dda4a5b2..b7275c63 100644 --- a/tracer_test.go +++ b/tracer_test.go @@ -70,9 +70,9 @@ func TestTracerSuite(t *testing.T) { func (s *tracerSuite) TestBeginRootSpan() { s.metricsFactory.Clear() startTime := time.Now() - s.tracer.(*tracer).timeNow = func() time.Time { return startTime } + s.tracer.(*Tracer).timeNow = func() time.Time { return startTime } someID := uint64(12345) - s.tracer.(*tracer).randomNumber = func() uint64 { return someID } + s.tracer.(*Tracer).randomNumber = func() uint64 { return someID } sp := s.tracer.StartSpan("get_name") ext.SpanKindRPCServer.Set(sp) @@ -189,7 +189,7 @@ func (s *tracerSuite) TestTraceStartedOrJoinedMetrics() { } for _, test := range tests { s.metricsFactory.Clear() - s.tracer.(*tracer).sampler = NewConstSampler(test.sampled) + s.tracer.(*Tracer).sampler = NewConstSampler(test.sampled) sp1 := s.tracer.StartSpan("parent", ext.RPCServerOption(nil)) sp2 := s.tracer.StartSpan("child1", opentracing.ChildOf(sp1.Context())) sp3 := s.tracer.StartSpan("child2", ext.RPCServerOption(sp2.Context())) @@ -218,12 +218,12 @@ func (s *tracerSuite) TestSetOperationName() { } func (s *tracerSuite) TestSamplerEffects() { - s.tracer.(*tracer).sampler = NewConstSampler(true) + s.tracer.(*Tracer).sampler = NewConstSampler(true) sp := s.tracer.StartSpan("test") flags := sp.(*Span).context.flags s.EqualValues(flagSampled, flags&flagSampled) - s.tracer.(*tracer).sampler = NewConstSampler(false) + s.tracer.(*Tracer).sampler = NewConstSampler(false) sp = s.tracer.StartSpan("test") flags = sp.(*Span).context.flags s.EqualValues(0, flags&flagSampled) @@ -231,7 +231,7 @@ func (s *tracerSuite) TestSamplerEffects() { func (s *tracerSuite) TestRandomIDNotZero() { val := uint64(0) - s.tracer.(*tracer).randomNumber = func() (r uint64) { + s.tracer.(*Tracer).randomNumber = func() (r uint64) { r = val val++ return @@ -261,16 +261,18 @@ func TestTracerOptions(t *testing.T) { TracerOptions.TimeNow(timeNow), TracerOptions.RandomNumber(rnd), TracerOptions.PoolSpans(true), + TracerOptions.Tag("tag_key", "tag_value"), ) defer closer.Close() - tracer := openTracer.(*tracer) + tracer := openTracer.(*Tracer) assert.Equal(t, log.StdLogger, tracer.logger) assert.Equal(t, t1, tracer.timeNow()) assert.Equal(t, uint64(1), tracer.randomNumber()) assert.Equal(t, uint64(1), tracer.randomNumber()) assert.Equal(t, uint64(1), tracer.randomNumber()) // always 1 assert.Equal(t, true, tracer.options.poolSpans) + assert.Equal(t, opentracing.Tag{Key: "tag_key", Value: "tag_value"}, tracer.Tags()[0]) } func TestInjectorExtractorOptions(t *testing.T) { diff --git a/transport_udp_test.go b/transport_udp_test.go index b18d52db..f9f5c1cc 100644 --- a/transport_udp_test.go +++ b/transport_udp_test.go @@ -35,7 +35,7 @@ import ( var ( testTracer, _ = NewTracer("svcName", NewConstSampler(false), NewNullReporter()) - jaegerTracer = testTracer.(*tracer) + jaegerTracer = testTracer.(*Tracer) ) func getThriftSpanByteLength(t *testing.T, span *Span) int { @@ -47,7 +47,7 @@ func getThriftSpanByteLength(t *testing.T, span *Span) int { return transport.Len() } -func getThriftProcessByteLengthFromTracer(t *testing.T, tracer *tracer) int { +func getThriftProcessByteLengthFromTracer(t *testing.T, tracer *Tracer) int { process := buildJaegerProcessThrift(tracer) return getThriftProcessByteLength(t, process) } diff --git a/zipkin.go b/zipkin.go index feca1371..3667c9fb 100644 --- a/zipkin.go +++ b/zipkin.go @@ -46,7 +46,7 @@ type InjectableZipkinSpan interface { } type zipkinPropagator struct { - tracer *tracer + tracer *Tracer } func (p *zipkinPropagator) Inject(