-
Notifications
You must be signed in to change notification settings - Fork 860
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
Proof of Concept of "Typed Spans" #195
Conversation
[skip ci] Primary goal is to maintain a simple and semantic interface for the code using it. See `HttpClientTracerDemonstration` for example usage.
…act at build time. [skip ci]
|
||
import io.opentelemetry.context.Scope; | ||
|
||
class HttpClientTracerDemonstration { |
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.
See here for example usage.
My $0.02: This is also great for testability. The entire instrumentation can easily be unit tested completely decoupled from any bytecode manipulation. I'm assuming your aim is eventually to move all the typed spans to opentelemetry-java and turning the auto-project into more or less a bunch of Advice classes that delegate all the actual work to the typed spans and tracers. I love that! My only VERY minor concern is that there may be a lot of boilerplate code to write for defining the tracer and span types for each instrumentation. |
// span.onResponse("response instance"); // implicitly called on end. | ||
span.end(response); | ||
} catch (Exception ex) { | ||
span.end(ex); |
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 like end()
taking either response or throwable, makes usage very clear 👍
SampleHttpServerTypedTracer tracer = new SampleHttpServerTypedTracer(); | ||
|
||
SampleHttpServerTypedSpan span = tracer.startSpan("request instance"); | ||
// span.onRequest("request instance"); // implicitly called on start. |
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.
Can onRequest()
and onResponse()
be protected
so it's not confusing whether or not they need to be called?
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.
Ok, I redid the package structure to allow this... I had to put the tracer and the span in the same package for it to work.
@Override | ||
public void end() { | ||
delegate.end(); | ||
} | ||
|
||
@Override | ||
public void end(EndSpanOptions endOptions) { | ||
delegate.end(endOptions); | ||
} |
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.
It would be nice if BaseTypedSpan
didn't have these two end()
methods, so that we could have some typed span hierarchies where they don't exist and it's super clear that you have to call either end(response)
or end(throwable)
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.
The problem is it extends from Span
, which allows it to be put into Scope
as-is. The alternative would be to override the method and throw an exception.
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.
Good point. I want to say maybe override and mark @Deprecated
so that IDEs will give hint that you shouldn't be calling it, but @Deprecated
isn't quite right, any idea some other way to get IDEs to hint that you shouldn't call it?
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.
Oh, I found example where I'd seen this kind of thing before, Guava's ImmutableList:
/**
* Guaranteed to throw an exception and leave the list unmodified.
*
* @throws UnsupportedOperationException always
* @deprecated Unsupported operation.
*/
@Deprecated
@Override
public final void add(int index, E element) {
throw new UnsupportedOperationException();
}
I think it would be helpful to add this kind of override for end()
and end(endOptions)
methods in ClientTypedSpan
and ServerTypedSpan
to help avoid confusion of which end(...)
method to call.
My extra comment above isn't showing down here, so copying it:
Although API is not supposed to throw exceptions, and we're hoping these might someday be there, so maybe mark deprecated, but no-op instead of throw exception?
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.
Although API is not supposed to throw exceptions, and we're hoping these might someday be there, so maybe mark deprecated, but no-op instead of throw exception?
agent-tooling/src/main/java/io/opentelemetry/auto/typed/span/HttpClientTypedSpan.java
Outdated
Show resolved
Hide resolved
import io.opentelemetry.trace.Span; | ||
|
||
public abstract class ClientTypedTracer< | ||
T extends ClientTypedSpan<T, REQUEST, RESPONSE>, REQUEST, RESPONSE> |
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.
Eeek 😆
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.
yeah, there wasn't a good way to ensure the return types were strongly typed without some extra generics complexity.
agent-tooling/src/main/java/io/opentelemetry/auto/typed/span/HttpClientTypedSpan.java
Outdated
Show resolved
Hide resolved
@prydin for the amount of code that would need to be written for each integration, see the I think there's still some work remaining to ensure that the API is flexible enough for all our needs, but I think it's a good starting point. |
FWIW, boilerplate doesn't bother me (I'm probably just numb 😁) |
…lity. And move setter method to tracer.
super.end(); | ||
} | ||
|
||
protected abstract T self(); |
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 doesn't look like it's used anymore
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 think it will still be necessary if we override any methods that return T.
@Override | ||
public void end() { | ||
delegate.end(); | ||
} | ||
|
||
@Override | ||
public void end(EndSpanOptions endOptions) { | ||
delegate.end(endOptions); | ||
} |
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.
Oh, I found example where I'd seen this kind of thing before, Guava's ImmutableList:
/**
* Guaranteed to throw an exception and leave the list unmodified.
*
* @throws UnsupportedOperationException always
* @deprecated Unsupported operation.
*/
@Deprecated
@Override
public final void add(int index, E element) {
throw new UnsupportedOperationException();
}
I think it would be helpful to add this kind of override for end()
and end(endOptions)
methods in ClientTypedSpan
and ServerTypedSpan
to help avoid confusion of which end(...)
method to call.
My extra comment above isn't showing down here, so copying it:
Although API is not supposed to throw exceptions, and we're hoping these might someday be there, so maybe mark deprecated, but no-op instead of throw exception?
Related to open-telemetry/opentelemetry-java#964 |
super(delegate); | ||
} | ||
|
||
protected abstract T onRequest(REQUEST request); |
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 would not provide this method and instead force the user to provide the request already in startSpan, so that all attributes are set on the builder and thus available to samplers (and onStart callbacks of SpanProcessors)
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 method is not exposed to the user... it's intended for implementation by the concrete class. Sounds like you think it's not necessary though?
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.
Discussed in weekly call. This looks like a good place to start iterating from. Thanks @tylerbenson!
SampleHttpServerTypedSpan span = tracer.startSpan("request instance"); | ||
// span.onRequest("request instance"); // implicitly called on start. | ||
|
||
try (Scope scope = tracer.withSpan(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.
suggestion: make a static class Request and Response as tracer.startSpan("request instance");
looks very much like a normal start of a span with a pre-canned name
Primary goal is to maintain a simple and semantic interface for the code using it. See
TypedTracerDemonstration
for example usage.It's proposed that these would replace the current decorator hierarchy.