-
I'm trying to figure out if this is expected or not. I have Kafka producer & consumer apps which use the OpenTelemetry otlp exporter extension. The apps are reactive apps. The producer app listens to REST requests (HTTP POST) using RESTEasy reactive. It then uses the @WithSpan("FightService.persistFight")
Uni<Fight> persistFight(@SpanAttribute("arg.fight") Fight fight) {
Log.debugf("Persisting a fight: %s", fight);
return Fight.persist(fight)
.replaceWith(fight)
.map(this.fightMapper::toSchema)
.chain(this.emitter::send)
.replaceWith(fight);
} In the consumer app, it does this: @Incoming("fights")
@Outgoing("team-stats")
@WithSpan("SuperStats.computeTeamStats")
public Multi<TeamScore> computeTeamStats(Multi<Fight> results) {
return results.map(this.stats::add)
.invoke(score -> LOGGER.debugf("Fight received. Computed the team statistics: %s", score));
} The outgoing channel goes to an in-memory channel which is then served over a websocket. When I issue the HTTP post to the producer app and look at the trace in Jaeger, I don't see the relationship with the consumer app: But when I look at the consumer app, I do see a "receive" operation where I do see the relationship: Going back to the producer in the Jaeger UI, I now see a "send" operation where the relationship does show: 2 parts to my question(s): Part 1Is this a bug? Is it working as expected? Am I missing something in my implementation to get this to work? Part 2Additionally, are the spans really working the way they should? In Shouldn't the @WithSpan("FightService.performFight")
public Uni<Fight> performFight(@SpanAttribute("arg.fighters") @NotNull @Valid Fighters fighters) {
Log.debugf("Performing a fight with fighters: %s", fighters);
return determineWinner(fighters)
.chain(this::persistFight);
}
@WithSpan("FightService.persistFight")
Uni<Fight> persistFight(@SpanAttribute("arg.fight") Fight fight) {
Log.debugf("Persisting a fight: %s", fight);
return Fight.persist(fight)
.replaceWith(fight)
.map(this.fightMapper::toSchema)
.map(f -> Message.of(f, Metadata.of(TracingMetadata.withPrevious(Context.current()))))
.chain(message -> {
this.emitter.send(message);
return Uni.createFrom().voidItem();
})
.replaceWith(fight);
} Or are they sequential due to the nature of reactive programming? Its essentially just assembling a pipeline of operations. The order/nesting of methods really doesn't affect the execution of the pipeline? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 24 replies
-
/cc @Ladicek, @alesj, @cescoffier, @ozangunalp, @radcortez |
Beta Was this translation helpful? Give feedback.
-
OTel is not fully integrated with RM yet. While everything is mostly in place, we are still missing the piece to automatically propagate the context to and from RM. Right now this has to be done manually with: // outgoing
Message.of(OBJECT, Metadata.of(TracingMetadata.withCurrent(Context.current())))
// incoming
message.getMetadata(TracingMetadata.class).ifPresent(metadata -> QuarkusContextStorage.INSTANCE.attach(metadata.getCurrentContext())); |
Beta Was this translation helpful? Give feedback.
-
For the REST Client invocations what happens is that a single context is created to handle the execution, so multiple calls will use the same context. Due to the Span suppression rules from OTel (subsequent spans from the same kind in the same context are ignored), you only get a single span for the REST Client calls. This is fixed in #25336 |
Beta Was this translation helpful? Give feedback.
OTel is not fully integrated with RM yet. While everything is mostly in place, we are still missing the piece to automatically propagate the context to and from RM. Right now this has to be done manually with: