-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide a tracer wrapper that can be used as basis for adding ex… #9
Conversation
…tension APIs (e.g. Observer) implementations
@jpkrohling @pavolloffay Can you review please? |
public Span setOperationName(String operationName) { | ||
wrappedSpan.setOperationName(operationName); | ||
this.operationName = operationName; | ||
if (!observers.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this check needed? Won't the for
loop just be skipped anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was to avoid creating the Iterator
- although the only purpose of this wrapper (currently) is to manage observers - so redundant at the moment. So will remove.
*/ | ||
public APIExtensionsSpan(Span span, String operationName, | ||
long startTimestampMicro, long startTimeNano, Map<String,Object> tags) { | ||
this.wrappedSpan = span; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to add some sanity checks here, like a null check for the span
. Otherwise, it might fail later on with a NPE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it to package private as should only be used from the APIExtensionsSpanBuilder
, which uses startManual
which must return a Span
.
@Override | ||
public Span setBaggageItem(String name, String value) { | ||
wrappedSpan.setBaggageItem(name, value); | ||
if (!observers.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as before: is this check needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(there are other places like this, so, if you do change this, the other places might need changing as well)
@Override | ||
public Span log(Map<String, ?> fields) { | ||
wrappedSpan.log(fields); | ||
return handleLog(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), fields); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you replace the System.currentTimeMillis
with a Clock
? This way, you don't lose the micro precision (not to mention that it's far easier to test with Clock
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you do change this to use a Clock, there are other places that would need to be changed as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately Clock
is 1.8, and we are trying to remain 1.6 compatible for now. Hopefully that decision won't last forever.
@Override | ||
public String getStringTag(String key) { | ||
Object value = tags.get(key); | ||
if (value instanceof String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather to a toString()
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was discussed in the PR related to the API, and decided that non-string tags should not be returned as strings, as null is used as a way to understand it is not a string type.
private final String operationName; | ||
private final SpanBuilder wrappedBuilder; | ||
private long startTimestampMicro = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); | ||
private long startTimeNano = System.nanoTime(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you have a nano and a micro?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Micro is for the timestamp - nano is for the elapsed time, to provide more accurate result.
public APIExtensionsSpanBuilder(Tracer tracer, List<TracerObserver> observers, | ||
String operationName, SpanBuilder builder) { | ||
this.tracer = tracer; | ||
this.observers = observers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are letting the caller retain access to this list. What would happen if the caller changes this list down the road? Is that OK, or would it be better to clone the list?
Also, it might be worth doing some sanity checks (null checks) as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with previous comment - I've now made this package private and is only used by the APIExtensionsTracer
which owns the list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose that still leaves the potential for the app to add/remove an observer which would impact inflight spans. So probably better to clone to make sure the observer sees the full lifecycle of the span being observed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, actually it won't be a problem - the list is only used at the point where the span is started, at which point it obtain a SpanObserver
which will monitor the complete lifecycle of the span. As said before the list is maintained by the extension tracer - the app could add/remove observers at any point - but essentially at the point the span is started, it has a snapshot of the observer list at that point in time.
} | ||
|
||
Map<String, Object> tags() { | ||
return tags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are giving the caller access to your internal map. Is that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for test access only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@objectiser Can't you fine another way to test, without exposing internals ?
I think the question is justified why this method exists.
private final Tracer wrappedTracer; | ||
private final List<TracerObserver> observers = new CopyOnWriteArrayList<TracerObserver>(); | ||
|
||
public APIExtensionsTracer(Tracer tracer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about providing a constructor that tries to get a Tracer from the GlobalTracer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above - prefer to leave to app for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@objectiser I agree, I think most usecases for wrapping with API extensions will be from application initialization code just before setting the GlobalTracer
.
Also, you're not preventing the caller from supplying the GlobalTracer anyway if he/she does have such a usecase.
@Override | ||
public Number getNumberTag(String key) { | ||
Object value = tags.get(key); | ||
if (value instanceof Number) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I try to get a "int" from a string value, I would expect an exception, not null (at least, that's what happen in other APIs out there). If returning null
is indeed what you think your callers would expect (or want), then please add a javadoc about it (sorry if it's added somewhere already)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/opentracing-contrib/java-api-extensions/blob/master/opentracing-api-extensions/src/main/java/io/opentracing/contrib/api/SpanData.java#L87
If you think the API should be different, then best to raise an issue so can be discussed.
… remove check for empty observer list
@jpkrohling Thanks! |
@Override | ||
public long getDuration() { | ||
// If start or finish nano times are not available, then use timestamps | ||
if (startTimeNano == 0 || finishTimeNano == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this can be 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
closes #5 |
…tension APIs (e.g. Observer) implementations