Skip to content
This repository has been archived by the owner on May 23, 2024. It is now read-only.

Add span context to OnStartSpan #159

Merged
merged 20 commits into from
Jul 13, 2017

Conversation

hkshaw1990
Copy link
Contributor

Some observers will need to call span functions from within the observed span events.

@CLAassistant
Copy link

CLAassistant commented Jun 12, 2017

CLA assistant check
All committers have signed the CLA.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 85.355% when pulling 8bd55d8 on hkshaw1990:observer-with-spancontext into d649bde on uber:master.

observer.go Outdated
@@ -24,7 +24,7 @@ import opentracing "github.com/opentracing/opentracing-go"

// Observer can be registered with the Tracer to receive notifications about new Spans.
type Observer interface {
OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver
OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) SpanObserver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a problem with changing this interface - it is a breaking change and would require a major version upgrade. Given opentracing-contrib/go-observer#1, what if we leave this unchanged (document as deprecated) and instead add support for "options.ContribObserver()"?

We will support both until the next major release of jaeger-client, at which point we can consolidate into one AP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yurishkuro Agreed and thanks for the suggestion.
Have added a commit which reverts this and adds ContribObserver.

@@ -24,6 +24,8 @@ import (
"github.com/uber/jaeger-lib/metrics"

"github.com/uber/jaeger-client-go"

otobserver "github.com/opentracing-contrib/go-observer"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer not to have an explicit dependency on this package, but instead just re-define its interfaces here. Due to duck typing it should still be compatible, but Jaeger will be isolated from the potential API changes in the contrib package.

Copy link
Contributor Author

@hkshaw1990 hkshaw1990 Jun 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I tried this with the latest commits I added here. And, it shows some compatibility issues, i.e., if I initialize a new observer based on go-observer and try to assign this observer with ContribObserver() in cfg.New(), I get a compatibility error :
have OnStartSpan(opentracing.Span, string, opentracing.StartSpanOptions) (otobserver.SpanObserver, bool)
want OnStartSpan(opentracing.Span, string, opentracing.StartSpanOptions) (jaeger.ContribSpanObserver, bool)

@@ -0,0 +1,90 @@
// Copyright (c) 2017 Uber Technologies, Inc.
// (c) 2017 IBM Corp.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you sign the CLA it assigns copyright to Uber, so please don't include this here, because it restricts our ability to manage licensing of Jaeger packages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know that. Updated.

@@ -64,8 +66,18 @@ func Reporter(reporter jaeger.Reporter) Option {

// Observer can be registered with the Tracer to receive notifications about new Spans.
func Observer(observer jaeger.Observer) Option {
old := jaeger.OldObserver{Obs: observer}
Copy link
Member

@yurishkuro yurishkuro Jun 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not good having OldObserver public. There are two ways we can handle this:

  1. duplicate ContribObserver option on the Tracer and do the wrapping there and only there, thus making oldObserver private
  2. or define an internal/observer package and declare a public type there - by convention people should not be depending on APIs under internal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the 1st option, since, the 2nd option will still make old observer public.

span.go Outdated
@@ -28,6 +28,8 @@ import (
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/opentracing/opentracing-go/log"

otobserver "github.com/opentracing-contrib/go-observer"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undesired dependency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

span.go Outdated
@@ -63,7 +65,7 @@ type Span struct {
// references for this span
references []Reference

observer SpanObserver
contribObserver otobserver.SpanObserver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can keep the name observer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, changed this.

@yurishkuro
Copy link
Member

Ok. I tried this with the latest commits I added here. And, it shows some compatibility issues

Hm, yes, that's unfortunate, duck typing doesn't work when you have an interface returning an interface.

But to me a dependency on a contrib package is a bigger issue, for semver compatibility reasons, especially on a contrib pkg that is brand new and not battle tested. We may need to do a compromise. The duck typing will still work on the SpanObserver, but for the main Observer the end user would have to write a small wrapper that implements jaeger.ContribObserver interface. I don't think it's a huge burden because it's just one function and the end user needs to have some custom code when initializing Jaeger tracer anyway.

So if you have a perf events related contrib.Observer, you would do something like this when passing it to Jaeger

type jaegerObserver struct {
    delegate *perfevents.Observer
}

// implement jaeger.ContribObserver
func (jo *jaegerObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (jaeger.ContribSpanObserver, bool) {
    return delegate.OnStartSpanRaw(sp, opName, ops)
}

as long as delegate.OnStartSpanRaw(sp, opName, ops) returns the raw *perfevents.SpanObserver (instead of the interface), the duck typing will work.

It's a bit of a dance, we can clean it up once the contrib.Observer gets more battle tested and the repo starts releasing semver.

@hkshaw1990
Copy link
Contributor Author

as long as delegate.OnStartSpanRaw(sp, opName, ops) returns the raw *perfevents.SpanObserver (instead of the interface), the duck typing will work.

@yurishkuro alright, seems ok. We can have that bit of the initialization code.

Any other comments on the added commits?

// var sp opentracing.Span
// sso := opentracing.StartSpanOptions{}
// spanObserver, ok := Observer.OnStartSpan(span, opName, sso);
// if ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be (idiomatic Go)

if spanObserver, ok := Observer.OnStartSpan(span, opName, sso); ok {
    // we have a valid SpanObserver
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this.

}

// ContribSpanObserver is created by the Observer and receives notifications
// about other Span events.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might add: It is meant to match http://...contrib-observer, but via duck typing, without directly importing the contrib package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this comment.

}

// contribSpanObserver is a dispatcher to other span observers
type contribSpanObserver struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/contribSpanObserver/compositeSpanObserver/ (we use "composite" in many places). compositeObserver at L63 as well.

I assume the nearly identical old dispatcher should be deleted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, changed the naming convention from contrib to composite and deleted the old dispatcher.

tracer.go Outdated
@@ -59,7 +59,7 @@ type tracer struct {
injectors map[interface{}]Injector
extractors map[interface{}]Extractor

observer observer
contribObserver contribObserver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: variable name should remain observer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed.


func (o contribObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) {
var spanObservers []ContribSpanObserver
for _, obs := range o.observers {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a TODO to optimize this for the case when ==1 span observer is created - it can be returned directly, without allocating the slice.

Copy link
Contributor Author

@hkshaw1990 hkshaw1990 Jun 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't entirely understand this part. Can you please elaborate?

}
}
if len(spanObservers) == 0 {
return noopContribSpanObserver, false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tracer has a direct dependency on this struct, so the function signature does not need to match the API. Specifically, the boolean value is meaningless because we always return a valid (albeit noop) span observer.

Copy link
Contributor Author

@hkshaw1990 hkshaw1990 Jun 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Nice suggestion. Updated in the latest commits.

tracer.go Outdated
@@ -233,7 +233,7 @@ func (t *tracer) startSpanWithOptions(

sp := t.newSpan()
sp.context = ctx
sp.observer = t.observer.OnStartSpan(operationName, options)
sp.observer, _ = t.contribObserver.OnStartSpan(sp, operationName, options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per comment above, there's no need for the second return value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

@omeid
Copy link

omeid commented Jun 23, 2017

Couldn't the span be just passed to a normal observer? I know this is breaking change but observer without access to span is not much of use really.
My use case is sending span logs to a different backend.

@yurishkuro
Copy link
Member

@omeid the "normal" observer is the interface we had before the new one in contrib, and it did not accept the Span. If you need a span you can just implement the new ContribObserver

@omeid
Copy link

omeid commented Jun 23, 2017

But this doesn't seem to be merged in, right? Also, how do I get a hang of the logs from a span? They seem to be unexported. :(

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.9%) to 84.454% when pulling b2ac245 on hkshaw1990:observer-with-spancontext into d649bde on uber:master.

Copy link
Member

@yurishkuro yurishkuro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor clean-up needed, otherwise lgtm

Note about pointers:

  • the compositeObserver is an addressable field in the tracer, it does not need to be declared as a pointer, but there's no reason to have its functions not use the pointer to avoid copying
  • the compositeSpanObserver instances should always be by pointer. When an instance of composite span observer is returned, the return type is an interface, so heap allocation will happen anyway, better to do that explicitly and avoid copying when calling functions on it.


// noopCompositeSpanObserver is used when there are no observers registered
// on the Tracer or none of them returns span observers from OnStartSpan.
var noopCompositeSpanObserver = compositeSpanObserver{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

take a pointer: = &compositeSpanObserver{}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

config/config.go Outdated
@@ -147,10 +147,14 @@ func (c Configuration) New(
jaeger.TracerOptions.ZipkinSharedRPCSpan(opts.zipkinSharedRPCSpan),
}

for _, obs := range opts.observers {
for _,obs := range opts.observers {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go fmt

obs Observer
}

func (o oldObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (o *oldObserver)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return compositeSpanObserver{observers: spanObservers}
}

func (o compositeSpanObserver) OnSetOperationName(operationName string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (o *compositeSpanObserver)

}
}

func (o compositeSpanObserver) OnSetTag(key string, value interface{}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (o *compositeSpanObserver)

observer.go Outdated
@@ -22,12 +22,16 @@ package jaeger

import opentracing "github.com/opentracing/opentracing-go"

// Observer can be registered with the Tracer to receive notifications about new Spans.
// Observer can be registered with the Tracer to receive notifications about
// new Spans. Note that this interface is now Deprecated, see:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the recommended format for deprecation notice https://rakyll.org/deprecated/

[blank line]
Deprecated: use jaeger.ContribObserver instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Made this and all the above changes except one.

observer.go Outdated
// SpanObserver is created by the Observer and receives notifications about other Span events.
// SpanObserver is created by the Observer and receives notifications about
// other Span events. Note that this interface is now Deprecated, see:
// github.com/opentracing-contrib/go-observer.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blank line]
Deprecated: use jaeger.ContribSpanObserver instead.

observer.go Outdated
@@ -39,50 +43,27 @@ type observer struct {
observers []Observer
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed? I thought compositeObserver above would be used instead,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The observer is probably needed since, it implements the Observer interface. If we remove this struct observer, the function OnStartSpan(operationName string, options opentracing.StartSpanOptions) i.e., without the span argument, can't be defined which in turn will affect the existing external observers, right? Is there any other way we can replace this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when old-style observers are passed to tracer or config via option, they are immediately converted to the contrib-style, thus we never need to use the old style observer, it is fully replaced by the new compositeObserver

}

// compositeObserver is a dispatcher to other observers
type compositeObserver struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to move this to observer.go (replace the old type). In the next major we can remove the contrib file/types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding moving this and replacing the old type, see below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replied below. The main reason to move it to observer.go is to reduce the size of the diff.

o.observers = append(o.observers, contribObserver)
}

func (o compositeObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) ContribSpanObserver {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (o *compositeObserver)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@yurishkuro
Copy link
Member

please update your master and rebase

hkshaw1990 added 12 commits July 6, 2017 20:22
Some of the observers will need to call span functions from within
the observed span events.
Reverts the previous commit (add span context to OnStartSpan) since, it
will break the existing observers. Adds another observer contribObserver
instead, implementing the interface
github.com/opentracing-contrib/go-observer.
Also renames the contribObserver -> observer in the Span struct.
Change the global interfaces back to contrib observer. Only the private
struct implementations remain as composite*.
This patch also fixes a test error by changing spanObserver to
compositeSpanObserver.
@hkshaw1990 hkshaw1990 force-pushed the observer-with-spancontext branch from f8643e5 to cb923e2 Compare July 6, 2017 15:15
config/config.go Outdated
@@ -147,12 +147,16 @@ func (c Configuration) New(
jaeger.TracerOptions.ZipkinSharedRPCSpan(opts.zipkinSharedRPCSpan),
}

for _, tag := range opts.tags {
tracerOptions = append(tracerOptions, jaeger.TracerOptions.Tag(tag.Key, tag.Value))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, merge issue. You need to define the official repo as eg upstream and

git checkout -b master
git pull upstream master
git checkout -b observer-with-spancontext
git rebase master

your master should be == upstream master

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, did the exact same thing, but I guess this crept on while resolving some conflicts. Will fix it.


return func(tracer *Tracer) {
tracer.observer.append(&old)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just delegate (DRY)

func (tracerOptions) Observer(observer Observer) TracerOption {
    return ContribObserver(&oldObserver{obs: observer})
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but delegation will require scope resolution here, since, ContribObserver is also an interface and a tracerOptions function. Are you ok with having this (asking this because it doesn't conform to the current convention for other functions in tracer_options.go)?

func (t *tracerOptions) Observer(observer Observer) TracerOption {
    return t.ContribObserver(&oldObserver{obs: observer})
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, yes delegate to scoped tracerOptions.ContribObserver function, so there's no naming conflicts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, updated.

}

func (o *oldObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) {
return o.obs.OnStartSpan(operationName, options), true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spanObserver := o.obs.OnStartSpan(operationName, options)
return spanObserver, spanObserver != nil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the change.


// noopCompositeSpanObserver is used when there are no observers registered
// on the Tracer or none of them returns span observers from OnStartSpan.
var noopCompositeSpanObserver = &compositeSpanObserver{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/noopCompositeSpanObserver/noopSpanObserver/ - it is not relevant that it is "composite", just that it's "no-op"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

@hkshaw1990 hkshaw1990 Jul 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, I am not able to reply to the previous comment, so, I will add my reply here:

replied below. The main reason to move it to observer.go is to reduce the size of the diff.

Got it. So, it does reduce the diff when the compositeObserver struct and its functions are moved to observer.go. But, to be clear, did you ask me to move all of compositeObserver and compositeSpanObserver code to observer.go leaving only the ContribObserver and ContribSpanObserver interfaces there?

observer.go Outdated
@@ -39,50 +43,27 @@ type observer struct {
observers []Observer
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when old-style observers are passed to tracer or config via option, they are immediately converted to the contrib-style, thus we never need to use the old style observer, it is fully replaced by the new compositeObserver

}

// compositeObserver is a dispatcher to other observers
type compositeObserver struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replied below. The main reason to move it to observer.go is to reduce the size of the diff.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 85.093% when pulling 59e4a99 on hkshaw1990:observer-with-spancontext into c0cb42c on uber:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 85.093% when pulling b06b8a1 on hkshaw1990:observer-with-spancontext into c0cb42c on uber:master.

@yurishkuro yurishkuro merged commit feadba6 into jaegertracing:master Jul 13, 2017
@yurishkuro
Copy link
Member

Thanks for your patience, @hkshaw1990 .

@yurishkuro yurishkuro mentioned this pull request Jul 29, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants