-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
OpenTelemetry Mutiny #25576
OpenTelemetry Mutiny #25576
Conversation
Add MutinyAsyncOperationEndStrategy
add some configuration to add some events or span attributes during certain mutiny events (onSubscription/onCancellation) register async strategy in OpenTelemetry
…n during build time.
… case the async-mutiny dependency is not used or disabled no mutiny strategy will be registered and async strategies implementation from OpenTelemetry falls back to sync.
…ld need feedback here. Created Tracing Mutiny streams and subscriber
add some junit tests
…allows programmatic creation of "withSpan". Also supports async return values. Add @withroot annotation which allows to start a new trace when used in combination with @WithSpan. Also allows to link to previous active span context.
Some javadoc
add more junit tests
revert some line formatting executed on bom/pom.xml
/cc @radcortez |
This is the QuarkusContextStorage line I talked about above: Line 70 in 3ece1b1
|
I think what is still missing is a way to register to WithSpanInterceptor, so that it is possible to adjust / replace the instrumenter. I think this would be awesome. Why? Some tools ignore the Exception set by instrumenter but it works when attributes are set manually. But this would not be possible if the instrumenter is not adjustable. |
@radcortez @brunobat @jponge @cescoffier ping. There are conflicts now but it would be nice to check if the approach is worth pursuing. |
} | ||
|
||
private static Multi<String> createDefaultMulti(final SpanContext context, final int delayMs) { | ||
return Multi.createFrom().items("SUPERSONIC", "SUBATOMIC", "JAVA") |
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.
Both Uni and Multi tests use the "delayIt" operators which use the Quarkus scheduler.
It would be interesting to test:
- truly async using the duplicated context
- unmanaged thread.
import io.smallrye.mutiny.Multi; | ||
import io.smallrye.mutiny.infrastructure.MultiInterceptor; | ||
|
||
public class OpenTelemetryMultiInterceptor implements MultiInterceptor { |
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, this will have a massive impact on the performance. I don't know a better way, but that's something to keep in mind.
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 know. When I check other implementation like rxjava it seems like this is necessary:
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/rxjava/rxjava-2.0/library/src/main/java/io/opentelemetry/instrumentation/rxjava/v2_0/TracingAssembly.java
I do not know if there is a different way to achieve tracing in reactive. But I am afraid it is not. This is why I hoped that the Munity guys say: No, no Chris there is a way easier approach to achieve the same. Without that I always ended up with broken, not connected traces, which is not really helpful.
import io.smallrye.mutiny.infrastructure.UniInterceptor; | ||
import io.smallrye.mutiny.subscription.UniSubscriber; | ||
|
||
public class OpenTelemetryUniInterceptor implements UniInterceptor { |
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 for the Multi version - it has a massive performance cost.
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 comment above
if (multi instanceof TracingMulti) { | ||
return multi; | ||
} | ||
return new TracingMulti<>(Context.current(), multi); |
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.
Are you sure the Context.current is what you want?
It will be the current at assembly time. I'm pretty sure you need the one at subscription time.
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. Long time ago... I cannot really remember. It is also present in onSubscription. I mean the tests are there, so feel free to improve the approach. I quickly checked the rxjava impl. I do not see it there. There the current context is only used at subscription time. I think something was broken when I did not do this, but as said, I cannot really remember. I had some difficulties with the Multi instrumentation, that is at least what I can remember.
private final Subscriber<? super T> subscriber; | ||
|
||
public TracingMultiSubscriber(final Context context, final Subscriber<? super T> subscriber) { | ||
this.context = context; |
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 the assembly time context. Most probably you need the subscription time one (onSubscribe()).
public void onItem(final T item) { | ||
try (final Scope ignored = context.makeCurrent()) { | ||
subscriber.onNext(item); | ||
} |
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 it expected to close the scope? onNext is likely going to do async operation. So, the return of onNext does not mean we are done.
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.
We probably need to discuss what would be our preferred approach.
Looking only at what we have here:
- We do need more integration tests to make sure the context is being correctly propagated and that spans are generated as we expect, no matter the origin or the target
- The Extension cannot be something separate. It should be part of the main extension
- I share the same concerns as @cescoffier
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 need to discuss this, as stated above.
This should be part of the main extension.
The main extension is in the process of a massive rewrite: #30033
Hi in #25453 it was mentioned that OpenTelemetry does not work with Mutiny.
I already worked on this topic and thought maybe the stuff I created might be helpful. I am pretty sure that it is not 100% complete but maybe it can save some time.
I am also not sure about a few things, so I hope the Experts in Mutiny etc. can help here.
Maybe shortly how it works:
OpenTelemetry provides the so called AsyncOperationEndStrategy. So you can basically register different type (here Uni and Multi) and the strategy then needs to make sure that spans are closed correctly.
Due to the fact that async strategies can be used in the same context as imperative things I adjusted the WithSpanInterceptor (Or to be more exact I moved some things around. I tried to explain why in code). So basically when you use WithSpan, the strategy checks if it can handle the return type. If so it does some magic. Otherwise, it falls back to imperative. Also errors close the span properly now.
Besides that the main Topic is to get the suitable context on different events from Mutiny (onItem, onComplete, etc.) Therefore, I used the Mutiny Interceptor approach to wrap the created Unis. I tried to follow the approach OpenTelemetry does for rxjava. And here I really hope that the Mutiny Experts say: "What? Why so complicated that is much easier to achieve. Just do this and that."
What else:
QuarkusContextStorage
In general I wrote some TODOs: which also contain some questions but there is one thing I am not that sure about and this is the QuarkusContextStorage. During Scope creation it also checks onClose if the current Context is the one which was used during attach. In my tests on a project, this was not working well in cobination with the Vertx SQL traces. I am also not sure why this happens. Seems like a span with scope is started but closed at transaction commit (I think), but then might overlap with scopes from Mutiny etc. But maybe you see if I did a mistake here.
In general I am not that familiar with Quarkus extension development so I probably forgot something or the maven structure / dependencies are not suitable.
The build is still running (seems like to take some time) but I thought I could already create the PR so that you can take a look.
Ok this was more text than I thought. I hope it helps and feedback would be really awesome.