Skip to content
This repository has been archived by the owner on Jul 1, 2022. It is now read-only.

Allow subclassing of common classes #509

Merged
merged 1 commit into from
Aug 21, 2018

Conversation

isaachier
Copy link
Contributor

@isaachier isaachier commented Aug 6, 2018

Give protected access to core Jaeger classes so that users may extend them. The main motive here is to create a bridge between the legacy com.uber.jaeger classes to the io.jaegertracing classes (equivalent to a typedef in C/C++). Note, in general it would not be wise to implement a subclass of these classes because they are internal and may change without notice!

Additionally, implement abstract factory pattern to create instances of spans, span contexts, and span builders. This allows the caller to provide subclasses of the aforementioned classes. For example, when the tracer needs to create a JaegerSpanBuilder object, it invokes createSpanBuilder instead. createSpanBuilder acts as a hook to instantiate a subclass instead of the JaegerSpanBuilder base class.

@@ -462,7 +461,7 @@ public void apply(JaegerTracer.Builder builder) {
registerCodec(builder, Format.Builtin.TEXT_MAP);
}

protected void registerCodec(JaegerTracer.Builder builder, Format<TextMap> format) {
private void registerCodec(JaegerTracer.Builder builder, Format<TextMap> format) {
Copy link
Member

Choose a reason for hiding this comment

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

why this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Find/replace mistake. Will remove.

@codecov
Copy link

codecov bot commented Aug 6, 2018

Codecov Report

Merging #509 into master will increase coverage by 0.15%.
The diff coverage is 96.77%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #509      +/-   ##
============================================
+ Coverage     88.27%   88.42%   +0.15%     
- Complexity      500      505       +5     
============================================
  Files            65       66       +1     
  Lines          1850     1883      +33     
  Branches        239      239              
============================================
+ Hits           1633     1665      +32     
- Misses          140      142       +2     
+ Partials         77       76       -1
Impacted Files Coverage Δ Complexity Δ
...ain/java/io/jaegertracing/internal/JaegerSpan.java 93.45% <ø> (ø) 47 <0> (ø) ⬇️
...n/java/io/jaegertracing/internal/JaegerTracer.java 90.38% <100%> (+0.54%) 24 <4> (ø) ⬇️
...io/jaegertracing/internal/JaegerObjectFactory.java 100% <100%> (ø) 4 <4> (?)
...a/io/jaegertracing/internal/JaegerSpanContext.java 90.9% <100%> (+0.28%) 21 <4> (-1) ⬇️
.../src/main/java/io/jaegertracing/Configuration.java 94.6% <100%> (+0.43%) 42 <1> (+2) ⬆️
...egertracing/internal/propagation/TextMapCodec.java 91.11% <100%> (+0.52%) 22 <0> (ø) ⬇️
...ertracing/internal/propagation/B3TextMapCodec.java 85% <77.77%> (-1.54%) 19 <0> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4934922...e61b97c. Read the comment docs.

@@ -46,7 +46,7 @@
private List<LogData> logs;
private boolean finished = false; // to prevent the same span from getting reported multiple times

JaegerSpan(
protected JaegerSpan(
Copy link
Member

Choose a reason for hiding this comment

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

@isaachier I don't think this will be enough, is it? I.e. you'd also need an overridable method that actually creates the object.

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 if you accompany this with a unit test showing the working subclassing.

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 added a lot more. The test definitely helped me determine what was necessary.

@isaachier isaachier changed the title Add protected methods to Configuration so subclasses may extend it Add protected methods to common classes so subclasses may extend them Aug 6, 2018
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.

https://www.youtube.com/watch?v=0z_Qqnq8pI8

You have added a ton of irrelevant formatting. Where is it coming from? Don't mix formatting changes with tbhe business logic changes.

@isaachier
Copy link
Contributor Author

Lol I'm not trying to. Travis CI kept failing the build because of the formatting and licensing issues.

@yurishkuro
Copy link
Member

If you need to fix formatting, then let's do it in a separate PR.

@isaachier
Copy link
Contributor Author

Undid the formatting fix for now.

}

private static class SpanContext extends JaegerSpanContext {
SpanContext(JaegerSpanContext ctx) {
Copy link
Member

Choose a reason for hiding this comment

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

That's not what I thought you were going after. If you extend the context, why do you need to accept it as agrument? The oss tracer currently does "new SpanContext". You can move that call into a function that you would override in the subclassed tracer and return your own subclass of SpanContext.

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 could redo a lot of code. The idea is to avoid code duplication by allowing base classes to do 99% of the work. Imagine the case of a JaegerTracer starting a JaegerSpan. There is a lot of logic dedicated to determining the span parent, etc. Now I could override start because Tracer needs to return Span instead of JaegerSpan, but that's going to result in a huge amount of copy-paste. Isn't it easier to use a constructor Span(JaegerSpan span) (note package visibility, not public)?

Copy link
Member

Choose a reason for hiding this comment

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

I am not suggesting redoing any code. What I am saying is that, for example, Trace has this code in builder.createNewContext():

return new JaegerSpanContext(id, id, 0, flags);

createNewContext itself is a large method we don't want to replicate. But we can change the above line to

return instantiateNewSpanContext(id, id, 0, flags);

and add method:

protected JaegerSpanContext instantiateNewSpanContext(...) {
  return new JaegerSpanContext(id, id, 0, flags);
}

Then in your subclassed Tracer you just need to override this method

@Override
protected JaegerSpanContext instantiateNewSpanContext(...) {
  return new SubclassedJaegerSpanContext(id, id, 0, flags);
}

Your subclassed tracer will have a total of 3 methods: constructor that directly delegates to super(....same args.....), and two instantiate methods, for span context and the Span that create
subclassed instances.

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 now I understand. I think this is called the abstract factory pattern. That definitely makes sense. I am usually hesitant to change so much of the existing code, but I guess this is a better design.

@@ -653,19 +652,19 @@ public static SenderConfiguration fromEnv() {
}
}

private static String stringOrDefault(String value, String defaultValue) {
protected static String stringOrDefault(String value, String defaultValue) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you making them protected so that you can use them in subclasses? Those are not part of the business of this class, I'd rather see them in a, say, StringUtils class (as much as a I dislike utils classes).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly, my subclass doesn't use these. I thought if they were static private utility methods here, then they ought to be static protected for subclasses. Nonetheless, I was considering adding a new class called ConfigUtils or something just for that purpose.

import java.util.Map;

public class JaegerSubclassTest {
private static class Configuration extends io.jaegertracing.Configuration {
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/Configuration/CustomConfiguration/ (or anything else that won't class with i.j.Configuration)

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

}
}

private static class Tracer extends JaegerTracer {
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/Tracer/CustomTracer/ (or anything else that won't clash with io.opentracing.Tracer, which is part of the hierarchy)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely

}
}

Tracer(io.jaegertracing.internal.JaegerTracer tracer) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You probably don't need the fully qualified class name here, do you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch.

}
}

private static class Span extends JaegerSpan {
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/Span/CustomSpan

final Configuration config = new Configuration("test-service");
final Tracer tracer = config.getTracer();
final Scope scope = tracer.buildSpan("test-operation").startActive(true);
Assert.assertNotNull(tracer.scopeManager().active());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand that if this assertion works, then a bunch of other stuff has also worked, but it would be nice to have your "base" assertions made explicit here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya this test isn't perfect. Needs work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be a bit better now.

}

protected TracingFactory tracingFactory() {
return new TracingFactory();
Copy link
Member

Choose a reason for hiding this comment

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

factory should be instantiated only once

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 was thinking about using a singleton. I guess I should.

Copy link
Member

Choose a reason for hiding this comment

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

not a singleton in the sense of a private static member, but just an externally provided object.

return config;
}

protected void initFromEnv() {
Copy link
Member

Choose a reason for hiding this comment

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

why? this is not the code we want to repeat, so why is it factored out into a method?

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 because Configuration has no factory method at the moment. All these static methods are problematic because they construct the concrete type instead of the subclass. In general, I was able to solve by passing or overriding the factory. I can try doing the same here.

Copy link
Member

Choose a reason for hiding this comment

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

just an idea - while subclassing tracer / span is necessary, I don't think the same holds for the configuration class. It's fine to wrap it, and not provide all the functionality that exists in the oss. For example, internally we don't init from env.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I reverted the protected methods back to private.


public JaegerSpanContext(long traceId, long spanId, long parentId, byte flags) {
this(traceId, spanId, parentId, flags, Collections.<String, String>emptyMap(), null);
}

JaegerSpanContext(
protected JaegerSpanContext(
Copy link
Member

Choose a reason for hiding this comment

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

who would use this ctor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It isn't exactly popular, but it is the more complete constructor. If the subclass wants to override the other constructor, it would need to use this constructor and pass in a custom factory for the last argument.

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, now that I see the constructor I understand what you mean. Will remove.

@@ -107,7 +120,11 @@ public String toString() {
return contextAsString();
}

public static JaegerSpanContext contextFromString(String value)
public static JaegerSpanContext contextFromString(String value) {
Copy link
Member

Choose a reason for hiding this comment

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

who uses this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well it is public and this isn't the legacy client, so I feel uncomfortable deleting it or adding a required argument. It is used internally to decode a span context from a TextMap.

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 deprecate this method. I think the only reason it is public is because Codecs are in a different package. Since you already have factory.createSpanContext() method, the only thing this is doing is parsing a string representation of the span context - arguably a function that should live in the codecs themselves, since there can be different representations like the upcoming W3C Trace Context spec. So I suggest refactoring this and moving the main logic to TextMapCodec (it can be a public static method there, always taking the factory).

Copy link
Contributor Author

@isaachier isaachier Aug 8, 2018

Choose a reason for hiding this comment

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

I'll deprecate it and refactor logic in TextMapCodec.


private JaegerTracer(
protected JaegerTracer(
Copy link
Member

Choose a reason for hiding this comment

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

I don't see why you need to duplicate constructors - it was private previously, nobody called it externally, so it should be ok to change its API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I'll remove this one and any others I find that were originally private or package scope.

@@ -177,10 +179,14 @@
*/
private JaegerTracer tracer;

public Configuration(String serviceName) {
public Configuration(String serviceName, TracingFactory tracingFactory) {
Copy link
Member

Choose a reason for hiding this comment

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

did you see my comment? #509 (comment) ?

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 did. I'll comment more there. Need the tracing factory to construct the JaegerTracer.Builder subclass used in the config to create the tracer.

withTracerTags(tracerTagsFromEnv())
.withReporter(ReporterConfiguration.fromEnv())
.withSampler(SamplerConfiguration.fromEnv())
.withCodec(CodecConfiguration.fromEnv());
Copy link
Member

Choose a reason for hiding this comment

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

doesn't look like there's a change here, please undo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea.

new BigInteger(parts[3], 16).byteValue());
new BigInteger(parts[3], 16).byteValue(),
Collections.<String, String>emptyMap(),
null);
Copy link
Member

Choose a reason for hiding this comment

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

what is null? should it be factory?

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 actually a reference to debugId. But this should definitely take a TracingFactory argument as well. Will fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Never mind. The factory passes this internally to the JaegerSpanContext constructor.

@@ -166,14 +175,19 @@ boolean isDebugIdContainerOnly() {
* debug trace, and the value of header recorded as a span tag to serve as a searchable
* correlation ID.
*
* @param debugId arbitrary string used as correlation ID
* @param debugId arbitrary string used as correlation ID.
* @param factory tracing object factory.
Copy link
Member

Choose a reason for hiding this comment

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

The name TracingFactory was making me uncomfortable, I was thinking ObjectFactory, but it too was too generic. I think TracingObjectFactory is much cleaner name

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 I struggled with the names. JaegerFactory or JaegerObjectFactory could work too.

Copy link
Member

Choose a reason for hiding this comment

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

+1 JaegerObjectFactory

@@ -95,8 +99,10 @@ private JaegerTracer(
this.metrics = metrics;
this.zipkinSharedRpcSpan = zipkinSharedRpcSpan;
this.scopeManager = scopeManager;
this.baggageRestrictionManager = baggageRestrictionManager;
Copy link
Member

Choose a reason for hiding this comment

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

so, like, a bug? 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No actually just wasn't needed outside of constructor. Originally needed this when I had constructor CustomTracer(JaegerTracer tracer), but don't need this anymore. Will remove.

Copy link
Member

Choose a reason for hiding this comment

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

remove

@@ -315,7 +321,7 @@ private JaegerSpanContext createNewContext(String debugId) {
}
}

return new JaegerSpanContext(id, id, 0, flags);
return JaegerTracer.this.tracingFactory.createSpanContext(id, id, 0, flags, Collections.<String, String>emptyMap(), debugId);
Copy link
Member

Choose a reason for hiding this comment

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

I am seeing a mix of factory and tracingFactory. If you agree with my above comment to call it TracingObjectFactory, then I'd suggest field name objectFactory, to be used consistently

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 thought I renamed all factory references to tracingFactory. Will change to objectFactory throughout.

@@ -354,7 +360,7 @@ private JaegerSpanContext createChildContext() {
}
}

return new JaegerSpanContext(
return JaegerTracer.this.tracingFactory.createSpanContext(
Copy link
Member

Choose a reason for hiding this comment

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

just curious, why fully qualified accessor instead of jus tracingFactory.createSpanContext ? I think the latter is also visible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JaegerTracer.SpanBuilder is a non-static nested class. I am able to access the tracer's tracingFactory from the inner class. Personally, not a huge fan of non-static nested classes (prefer explicitly passing instance to static nested class instead).

import java.util.List;
import java.util.Map;

public class TracingFactory {
Copy link
Member

Choose a reason for hiding this comment

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

add javadoc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya. Just need to stabilize the API, then will do.

ScopeManager scopeManager,
BaggageRestrictionManager baggageRestrictionManager,
boolean expandExceptionLogs) {
return new JaegerTracer(
Copy link
Member

@yurishkuro yurishkuro Aug 8, 2018

Choose a reason for hiding this comment

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

per my comment on Configuration, I don't think this method needs to be in the factory. Here's what you can do instead, to reduce the surface area:

class Configuration {
  public JaegerTracer.Builder getTracerBuilder() {
-     JaegerTracer.Builder builder = tracingFactory.createTracerBuilder(serviceName)
+     JaegerTracer.Builder builder = this.createTracerBuilder(serviceName)
  }

  protected createTracerBuilder() // overridable method, no more dependency on the factory
}

similar thing with JaegerTracer.Builder - just implement a protected method to create the tracer.

The only time where you need the abstract factory pattern is for creating SpanBuilder/Span/SpanContext, because those are done all the time from many places. But Tracer.Builder and Tracer are only created once, there's no need to complicate that with abstract factory pattern.

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 thought about doing that, but sort of wanted to do everything in factory or everything as subclass methods. This makes sense as a compromise.

@@ -15,6 +15,7 @@
package io.jaegertracing;

import io.jaegertracing.internal.JaegerTracer;
import io.jaegertracing.internal.JaegerObjectFactory;
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't need this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya I use the Google formatting plugin that will adjust the import and formatting at once, so I held off to avoid the formatting issues from earlier.

references);
}

public JaegerSpanContext createSpanContext(long traceId,
Copy link
Member

Choose a reason for hiding this comment

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

formatting inconsistent with the previous method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya will fix formatting after main code is good to go.

public static JaegerSpanContext withDebugId(String debugId) {
return new JaegerSpanContext(0, 0, 0, (byte) 0, Collections.<String, String>emptyMap(), debugId);
return withDebugId(debugId, new JaegerObjectFactory());
Copy link
Member

Choose a reason for hiding this comment

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

pretty sure we can drop this method - it's internal package after all

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. If we can use that logic, that would simplify things in a few places.

@@ -95,8 +99,10 @@ private JaegerTracer(
this.metrics = metrics;
this.zipkinSharedRpcSpan = zipkinSharedRpcSpan;
this.scopeManager = scopeManager;
this.baggageRestrictionManager = baggageRestrictionManager;
Copy link
Member

Choose a reason for hiding this comment

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

remove

new BigInteger(parts[1], 16).longValue(),
new BigInteger(parts[2], 16).longValue(),
new BigInteger(parts[3], 16).byteValue());
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

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

since this is internal package, can probably just remove this altogether

public CustomSpan(
JaegerTracer tracer,
String operationName,
JaegerSpanContext context,
Copy link
Member

Choose a reason for hiding this comment

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

nit: this should be CustomSpanContext

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea.

byte flags,
Map<String, String> baggage,
String debugId,
JaegerObjectFactory objectFactory) {
Copy link
Member

Choose a reason for hiding this comment

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

CustomObjectFactory

}

@Override
public CustomSpanContext withFlags(byte flags) {
Copy link
Member

Choose a reason for hiding this comment

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

why do you need to override these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sadly the covariant return type adds a lot of boilerplate where I have to cast things.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For a practical example, here is what happens if I don't override them:

CustomSpan customSpan = anotherCustomSpan.withFlags(flags);  // Error: cannot assign JaegerSpan to CustomSpan.

It will probably break user code without overriding.

Copy link
Member

Choose a reason for hiding this comment

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

Are you worried that someone would write

com.uber.jaeger.SpanContext _ = spanCtx.withFlags()

? I highly doubt it, but ok, we can leave these as a form of example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For SpanContext, I found 19 cases in internal Uber code. For Span I found 281.

Copy link
Member

Choose a reason for hiding this comment

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

referring to the class, yes, but I doubt they use these methods. But ok if you want to keep them.

Copy link
Member

Choose a reason for hiding this comment

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

I'd just be skeptical about adding them to the actual subclass (not this test)

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 that helps me. Just a matter of seeing what code changes would be necessary.

}

private static class CustomSpanContext extends JaegerSpanContext {
public CustomSpanContext(
Copy link
Member

Choose a reason for hiding this comment

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

could it be private? go for minimum visibility

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 think so. Didn't want to change visibility from base class. Constructors should be okay with that.


@Override
public CustomTracer.CustomSpanBuilder createSpanBuilder(JaegerTracer tracer, String operationName) {
return ((CustomTracer)tracer).new CustomSpanBuilder(operationName);
Copy link
Member

Choose a reason for hiding this comment

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

Do you actually need to override span builder? Wouldn't the default one do the trick?

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 wish... I have not found a way to do it in Java. Maybe by somehow (ab)using a generic helper class to generate bridge methods that are JVM generated casts.

@yurishkuro
Copy link
Member

looking much better!

import java.util.List;
import java.util.Map;

public class JaegerObjectFactory {
Copy link
Member

Choose a reason for hiding this comment

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

What is a purpose of this class? Can you please document it here?

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 will very soon.

@ghost ghost assigned yurishkuro Aug 14, 2018
@ghost ghost added the review label Aug 14, 2018
@@ -107,44 +114,22 @@ public String toString() {
return contextAsString();
}

public static JaegerSpanContext contextFromString(String value)
Copy link
Member

Choose a reason for hiding this comment

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

why is this still here? Didn't you merge it as a separate PR already? Maybe rebase from 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.

No just haven't gotten to fixing it yet. Sorry.


this.version = loadVersion();

tags.put(Constants.JAEGER_CLIENT_VERSION_TAG_KEY, this.version);
if (tags.get(Constants.TRACER_HOSTNAME_TAG_KEY) == null) {
builder.tags.put(Constants.JAEGER_CLIENT_VERSION_TAG_KEY, this.version);
Copy link
Member

Choose a reason for hiding this comment

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

nit: instead of modifying builder.tags I would create a local variable as a copy

Map<String, String> tags = new HashMap<String, String>(builder.tags);

computeDurationViaNanoTicks,
tags,
references);
JaegerSpan jaegerSpan =
Copy link
Member

Choose a reason for hiding this comment

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

Just curious, why not JaegerSpan jaegerSpan = getObjectFactory().createSpan( on one line?
Would be less code changes overall.

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 was auto-formatted. I will try your style and see what the auto-formatter does with it.

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 let me use this format. Changed it for me.

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean? Last I recall formatting isn't changed as part of the build.

@@ -22,7 +22,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

class PropagationRegistry {
public class PropagationRegistry {
Copy link
Member

Choose a reason for hiding this comment

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

looking at https://github.com/jaegertracing/legacy-client-java/pull/20/files, I don't see it being constructed there. So why public?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My mistake. Forgot to remove.


@Override
protected JaegerTracer createTracer() {
return new CustomTracer(this);
Copy link
Member

Choose a reason for hiding this comment

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

neat! Managed without making builder fields public/protected :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya I forgot this feature to, but it worked.

@isaachier
Copy link
Contributor Author

Thanks for the review! I will update ASAP.

@yurishkuro
Copy link
Member

overall lgtm. Let's make sure to minimize the unnecessary changes (like formatting only, or the removal of SpanContext.fromString()). Also, what's with the broken build?

@isaachier
Copy link
Contributor Author

I think Travis is having issues with the checkstyle job. I reverted a change I made in a JavaDoc that crashed the checkstyle plugin.

import org.junit.Assert;
import org.junit.Test;

public class JaegerObjectFactoryTest {
Copy link
Member

Choose a reason for hiding this comment

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

technically, this is not testing the object factory, but the extension mechanism.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough. Will revert renaming.


@Test
public void testTracer() {
final CustomObjectFactory tracingObjectFactory = new CustomObjectFactory();
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 used?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will check.

@yurishkuro
Copy link
Member

please update PR description to more clearly explain the purpose of this change.

Copy link
Contributor

@vprithvi vprithvi left a comment

Choose a reason for hiding this comment

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

It looks like this diff has a lot of unnecessary formatting changes which makes it difficult to read. Could you move the formatting changes to it's own diff?

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like an unnecessary change

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 happened automatically somehow... will revert.

.append(Long.toHexString(traceId)).append(":")
.append(Long.toHexString(spanId)).append(":")
.append(Long.toHexString(parentId)).append(":")
.append(Long.toHexString(traceId))
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

);
}

@Override
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary move

* Previously this would've returned null from the extract method, but now it returns a dummy
* context with only debugId filled in.
*
* from extract() method. This happens in the situation when "jaeger-debug-id" header is
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

* Retrieves the currently active span from the {@link ScopeManager}. It cannot be guaranteed that this span
* will be a {@link JaegerSpan}, as other libraries might have set this active span there. Consumers expecting
* this to return a {@link JaegerSpan} should always check the type of the return and act accordingly.
* Retrieves the currently active span from the {@link ScopeManager}. It cannot be guaranteed that
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

*
* @return the currently active span from the {@link ScopeManager}
*/
@Override
public Span activeSpan() {
// the active scope might have been added there through an API extension, similar to what the OT java-metrics
// the active scope might have been added there through an API extension, similar to what the OT
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

@@ -219,14 +213,15 @@ public void close() {
private String operationName;
private long startTimeMicroseconds;
/**
* In 99% situations there is only one parent (childOf), so we do not want to allocate
* a collection of references.
* In 99% situations there is only one parent (childOf), so we do not want to allocate a
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

@@ -304,7 +299,7 @@ private JaegerSpanContext createNewContext(String debugId) {
tags.put(Constants.DEBUG_ID_HEADER_KEY, debugId);
metrics.traceStartedSampled.inc(1);
} else {
//TODO(prithvi): Don't assume operationName is set on creation
// TODO(prithvi): Don't assume operationName is set on creation
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

computeDurationViaNanoTicks,
tags,
references);
JaegerSpan jaegerSpan =
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean? Last I recall formatting isn't changed as part of the build.

@@ -326,7 +322,7 @@ private JaegerSpanContext createNewContext(String debugId) {
return references.get(0).getSpanContext().baggage();
}

for (Reference reference: references) {
for (Reference reference : references) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be an unnecessary change

@yurishkuro
Copy link
Member

I would like @jpkrohling or @pavolloffay or @objectiser to approve before we merge, since they've done the most work on Java client recently.

@isaachier
Copy link
Contributor Author

I'm cool with that. I will admit this review is blocking some work internally. Using snapshot releases isn't easy for the projects I am dealing with, so I'd like to wrap this up ASAP.

Copy link
Collaborator

@jpkrohling jpkrohling left a comment

Choose a reason for hiding this comment

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

The code looks OK to me. There are a couple of things that could be done to resolve an appropriate object factory, but that's optional.

I would also consider changing the method signatures to public instead of protected: if you expect people to extend, even in an unsupported way, then make their lives easier and allow them to extend the way they want. I got bitten recently by this on TracerResolver#resolve ;)

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2018, Uber Technologies, Inc
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: The Jaeger Authors

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. This happens to be copied from other files, but we should correct them.

@@ -129,6 +140,7 @@ public JaegerSpanContext extract(TextMap carrier) {

public static class Builder {
private String baggagePrefix = BAGGAGE_PREFIX;
private JaegerObjectFactory objectFactory = new JaegerObjectFactory();
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could use the service loader API here, so that having a JaegerObjectFactory in the classpath would be sufficient to use an alternate object factory. Look at how we did it for the MetricsFactory/Sender for a reference.

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 had actually been considering something along those lines, but didn't realize there were other implementations. Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

I feel service loader is an overkill here. The goal is to allow subclassing, which is not a runtime decision.

@@ -107,7 +117,7 @@ public String toString() {
return contextAsString();
}

public static JaegerSpanContext contextFromString(String value)
public static JaegerSpanContext contextFromString(String value, JaegerObjectFactory objectFactory)
Copy link
Collaborator

Choose a reason for hiding this comment

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

To avoid breaking current code (there should be none, but still), keep the old signature, add @Deprecated and use the new one with new JaegerObjectFactory() (or whatever the default value should be):

/**
* @deprecated see {@link #contextFromString(String, JaegerObjectFactory)}
*/
@Deprecated
public static JaegerSpanContext contextFromString(String value) {
  return contextFromString(value, new JaegerObjectFactory());
}

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 points out these internal classes aren't part of official API and users are very unlikely to be calling contextFromString directly.

@@ -0,0 +1,174 @@
/*
* Copyright (c) 2018, Uber Technologies, Inc
Copy link
Collaborator

Choose a reason for hiding this comment

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

:-)

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 thanks :).

@yurishkuro
Copy link
Member

I would also consider changing the method signatures to public instead of protected

That would extend the API surface, even if internal. What does it prevent if we keep them protected? A subclass can always increase visibility if it needs that.

Signed-off-by: Isaac Hier <ihier@uber.com>
@isaachier
Copy link
Contributor Author

I just squashed the commits and rebased them on top of latest master branch (includes TextMapCodec changes).

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.

LGTM

@yurishkuro yurishkuro merged commit e248ce0 into jaegertracing:master Aug 21, 2018
@ghost ghost removed the review label Aug 21, 2018
@isaachier isaachier deleted the override-config branch August 22, 2018 14:58
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