Skip to content
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

Merged
merged 4 commits into from
Jul 21, 2017

Conversation

objectiser
Copy link
Contributor

…tension APIs (e.g. Observer) implementations

…tension APIs (e.g. Observer) implementations
@objectiser
Copy link
Contributor Author

@jpkrohling @pavolloffay Can you review please?

@objectiser objectiser changed the title WIP: Provide a tracer wrapper that can be used as basis for adding ex… Provide a tracer wrapper that can be used as basis for adding ex… Jul 21, 2017
public Span setOperationName(String operationName) {
wrappedSpan.setOperationName(operationName);
this.operationName = operationName;
if (!observers.isEmpty()) {

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?

Copy link
Contributor Author

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;

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.

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'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()) {

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?

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);

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)

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

Copy link
Contributor Author

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) {

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.

Copy link
Contributor Author

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();

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?

Copy link
Contributor Author

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;

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.

Copy link
Contributor Author

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.

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 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.

Copy link
Contributor Author

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;

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?

Copy link
Contributor Author

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.

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) {

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?

Copy link
Contributor Author

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.

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) {

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@objectiser
Copy link
Contributor Author

@jpkrohling Thanks!

@objectiser objectiser merged commit b580eb7 into opentracing-contrib:master Jul 21, 2017
@objectiser objectiser deleted the tracer branch July 21, 2017 12:59
@Override
public long getDuration() {
// If start or finish nano times are not available, then use timestamps
if (startTimeNano == 0 || finishTimeNano == 0) {
Copy link
Collaborator

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@objectiser
Copy link
Contributor Author

closes #5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants