-
Notifications
You must be signed in to change notification settings - Fork 859
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
New Instrumenter API #2596
New Instrumenter API #2596
Conversation
…nstrumentation into instrumentation-extractor
…nstrumentation into instrumentation-extractor
…nstrumentation into instrumentation-extractor
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.
Overall I really like this proposition - the composition is so much nicer (and flexible!) than the inheritance 👍 👍 👍
Various observations & ideas:
- For some instrumentations (like DB instrumentations) we will probably have to create some DTOs that collect several objects into a
REQUEST
- I don't think that's bad though, and some instrumentations already do this because of various reasons. - Those instrumentations that have more lifecycle phases than just start & end will be a bit problematic: we'll have to work around all those loose
onRequest()
,onException()
,onSomething()
calls.
|
||
Span span = Span.fromContext(context); | ||
if (span.isRecording()) { | ||
ctx.log() | ||
.whenComplete() | ||
.thenAccept( | ||
log -> { | ||
clientTracer.getNetPeerAttributes().setNetPeer(span, ctx.remoteAddress()); | ||
|
||
long requestEndTimeNanos = requestStartTimeNanos + log.responseDurationNanos(); |
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 that the Armeria instrumentation was the only one that passed the start/end timestamps manually - can we remove that safely? I think it'd be great to simplify that bit.
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.
Decided at least for now to remove it and might rethink later
|
||
AttributesBuilder attributesBuilder = Attributes.builder(); | ||
for (AttributesExtractor<? super REQUEST, ? super RESPONSE> extractor : extractors) { | ||
extractor.onStart(attributesBuilder, 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.
That's just a minor thing, but using the AttributeSetter
interface could help us avoid creating additional Attributes
instance and copying to the span/spanBuilder.
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 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 the thought has come to mind but also with the caveat that indeed, I think it's very rare to share the same code for both SpanBuilder and Span which sounds like it's the case.
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's very rare to share the same code for both SpanBuilder and Span which sounds like it's the case.
That interface was added to reuse NetPeerUtils
methods, which was called in various situations 😄
NetAttributesExtractor
fixes that issue, so the only use case left that I can imagine is onRequest()
situation - which we have to solve or find a workaround for anyway.
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Outdated
Show resolved
Hide resolved
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; | ||
import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
||
public abstract class HttpAttributesExtractor<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.
Should we put HTTP-related classes in a ...instrumenter.http
subpackage? Same for DB/messaging/RPC attribute/span name extractors (in the future). The base package would contain just the base classes/interfaces
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.
Hmm - I think it means we wouldn't have SpanStatusExtractor.http()
(not in same package anymore) and just use HttpStatusExtractor.create()
or something. I'm wondering about this vs extracting an attributes
package with HttpAttributesExtractor
, NetAttributesExtractor
, etc, that does keep the public API together with some separation.
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.
Or HttpSemanticConventions.spanName()
/HttpSemanticConventions.status()
in the http
package
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Outdated
Show resolved
Hide resolved
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Outdated
Show resolved
Hide resolved
...-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/ServerInstrumenter.java
Show resolved
Hide resolved
import io.opentelemetry.context.propagation.ContextPropagators; | ||
import io.opentelemetry.context.propagation.TextMapGetter; | ||
|
||
public abstract class ServerInstrumenter<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.
There's one interesting thing that I've noticed when comparing this class to HttpServerTracer
: the tracer class has the STORAGE
type parameter and attachServerContext()
and getServerContext()
methods that interact with it.
This can actually be removed from the tracer/instrumenter, it's only used for manual context propagation and I think it actually SHOULDN'T be here - that's a completely separate concern.
(Also, not all server instrumentations implement it)
...brary/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTracingBuilder.java
Outdated
Show resolved
Hide resolved
...brary/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTracingBuilder.java
Outdated
Show resolved
Hide resolved
@anuraaga I've implemented several of my comments in this PR: anuraaga#20 |
thx @anuraaga and @mateuszrzeszutek for giving so much thought to this! I agree with all the big ideas 👍 once you're ready, I suggest we go for it: merge and start migrating instrumentation one-by-one, uncovering edge cases and working out the smaller stuff as we go |
* New Instrumenter API - InstrumenterBuilder * New Instrumenter API - InstrumenterBuilder - code review comments
…nstrumentation into instrumentation-extractor
…nstrumentation into instrumentation-extractor
…nstrumentation into instrumentation-extractor
Sorry for the delay, this should be ready for review |
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
Instrumenter( | ||
Tracer tracer, | ||
SpanNameExtractor<? super REQUEST> spanNameExtractor, | ||
SpanKindExtractor<? super REQUEST> spanKindExtractor, | ||
StatusExtractor<? super REQUEST, ? super RESPONSE> statusExtractor, | ||
List<? extends AttributesExtractor<? super REQUEST, ? super RESPONSE>> extractors, | ||
ErrorCauseExtractor errorCauseExtractor) { | ||
this.tracer = tracer; | ||
this.spanNameExtractor = spanNameExtractor; | ||
this.spanKindExtractor = spanKindExtractor; | ||
this.statusExtractor = statusExtractor; | ||
this.extractors = extractors; | ||
this.errorCauseExtractor = errorCauseExtractor; | ||
} |
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 long as this is package protected passing all the arguments is fine, but if we plan to make this public would suggest to have an Option
pojo to allow us to add more arguments.
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 wouldn't become public, that's why we have the builder
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
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.
@anuraaga please let me know if you need any help with implementing suggestions from comments, I'd like to help with this PR.
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
...ion-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/StatusExtractor.java
Outdated
Show resolved
Hide resolved
import io.opentelemetry.context.propagation.TextMapSetter; | ||
import java.util.List; | ||
|
||
final class ClientInstrumenter<REQUEST, RESPONSE> extends Instrumenter<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.
We should probably rename ClientInstrumenter
/ServerInstrumenter
to DownstreamPropagatingInstrumenter
/UpstreamPropagatingInstrumenter
(also see anuraaga#20 (comment))
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.
Ah yeah - I understood the comment, I am still on the fence on it. Considering how much more common server/client is than the other forms I wonder if it's not so bad to have server / client words even when it's actually consumer / producer. Was thinking of addressing this in a follow-up when using from messaging.
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.
Just to clarify: we can still have newServerInstrumenter(TextMapGetter)
, new(Upstream|Downstream)Instrumenter()
would be just for the situation when you have to pass a custom SpanKindExtractor
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 it's a tradeoff between introducing new concepts vs having precise terminology. If server and producer are close enough to be two ducks (i.e, if it quacks it's a duck) then consolidating might be ok. I'm not too sure on it yet.
...api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java
Outdated
Show resolved
Hide resolved
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Outdated
Show resolved
Hide resolved
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
...src/test/java/io/opentelemetry/instrumentation/api/instrumenter/HttpStatusExtractorTest.java
Outdated
Show resolved
Hide resolved
...ntelemetry/instrumentation/api/instrumenter/InetSocketAddressNetAttributesExtractorTest.java
Outdated
Show resolved
Hide resolved
testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpClientTest.groovy
Outdated
Show resolved
Hide resolved
…n/test/base/HttpClientTest.groovy Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
Thanks a lot @mateuszrzeszutek - I've left some thumbs on ones that seem trivial for this PR and comments on ones I lean towards following up. If you'd like to send a pr for them sounds great or else will address tomorrow |
…nstrumentation into instrumentation-extractor
…elemetry-java-instrumentation into instrumentation-extractor
…nstrumentation into instrumentation-extractor
This should be ready for another review now, and I think it's probaby a reasonable first try to iterate on in future PRs |
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 am currently rather concerned about the simplicity of building an instrumenter for a given instrumentation. E.g. when using builder it is not clear what pieces are actually mandatory: do I have to provide httpAttributesExtractor? If not how can I understand if I should? Maybe a proper example will reduce that confusion.
protected final void onStart(AttributesBuilder attributes, REQUEST request) { | ||
set(attributes, SemanticAttributes.HTTP_METHOD, method(request)); | ||
set(attributes, SemanticAttributes.HTTP_URL, url(request)); | ||
set(attributes, SemanticAttributes.HTTP_TARGET, target(request)); | ||
set(attributes, SemanticAttributes.HTTP_HOST, host(request)); | ||
set(attributes, SemanticAttributes.HTTP_ROUTE, route(request)); | ||
set(attributes, SemanticAttributes.HTTP_SCHEME, scheme(request)); | ||
set(attributes, SemanticAttributes.HTTP_USER_AGENT, userAgent(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.
From http semantic convention:
more than the required attributes can be supplied, but this is recommended only if they cannot be inferred from the sent ones.
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.
For this API, we just set what was provided indeed, I don't think we can model something like that at this layer since we don't know what can be inferred, or if we do then it needs to be more explicit in the spec I guess, that spec language is way too vague to be actually implementable I think.
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.
WDYM, vague? Spec gives several options for required attribute combinations and then warns agains duplication.
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.
Well these attributes are defined by the spec so it could say exactly which attributes should be filled. If it's the three sets of attributes and says attributes SHOULD NOT be filled from one set if another is filled, it would be clearer. I don't know either if this is a ordered list by priority. So vague ;)
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.
E.g, in that text server name is mentioned descriptively but it could just be said it should always be filled when available.
...tation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/Instrumenter.java
Show resolved
Hide resolved
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's probaby a reasonable first try to iterate on in future PRs
👍
...brary/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTracingBuilder.java
Show resolved
Hide resolved
@@ -1,5 +1,5 @@ | |||
apply from: "$rootDir/gradle/instrumentation-library.gradle" | |||
apply plugin: "net.ltgt.errorprone" | |||
// apply plugin: "net.ltgt.errorprone" |
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.
add a TODO here?
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.
Oops
…nstrumentation into instrumentation-extractor
Still draft quality but I think this is ready for some initial feedback
The biggest difference from the current APIs is avoiding inheritance in favor of stitching together parts. The notion is that there are only two big logic behavioral differences between instrumenters - server instrumenters extract context and client ones inject it. Other than that, it's just customization - HTTP semantic conventions drives the customization of span attributes, etc, but there is no big difference in logic it's sort of configuration. So instead of using inheritance, I encapsulate attribute extraction, span name extraction, and status extraction into composable parts. This allows two things that I think an inheritance version has trouble with
Resolve different conflicting semantic conventions. For example, AWS spans are notorious for having different functionality that needs to be reconciled per-method for determining span name from options of RPC, Messaging, Database, or Armeria also can be used both for RPC or just plain HTTP. So these different types of conventions don't fit neatly into an inheritance tree, we need to be able to compose them. I haven't added an example of this yet in this PR though
Allow user customization of points they need with builder pattern. I think this style allows keeping instrumentation classes private without losing customization, because customization can be defined in terms of the instrumentation-api. With inheritance, we'd probably need to expose instrumentation classes to allow methods to be overridden.
More than happy to hear thoughts, there are probably some terrible aspects of this too. Though most issues won't be ironed out until trying it on more instrumentation in future PRs.
/cc @as-polyakov