Simplify interop between instrumented java libraries and otel4s#340
Simplify interop between instrumented java libraries and otel4s#340iRevive wants to merge 1 commit intotypelevel:mainfrom
Conversation
There was a problem hiding this comment.
I have a couple of thoughts:
- these methods indirectly expose a decent amount of the API of
Local; would it be reasonable forOtelJavato explicitly expose theLocalContext? and if so, would it be reasonable to extend that upwards toOtel4s(probably promoting itsCtxtype member to a type parameter at the same time)? - the names—particularly
useJContextUnsafe—aren't super clear that they use or manipulate theLocalContext. are there decent names that could be clearer about that fact? perhaps something likewithLocalAsJContext(Unsafe?)rather thanuseJContextUnsafe?
overall, I love this PR. it addresses the same problem #214 tries to solve in a more limited but much simpler fashion, and is not held up for an unknown amount of time by reliance on an unreleased feature
| * span.storeInContext(JContext.current()).makeCurrent() | ||
| * // invoke java code, e.g. instrumented RabbitMQ Java client | ||
| * span.end() | ||
| * } | ||
| * } | ||
| * | ||
| * val span = jTracer.span("java-span").startSpan() | ||
| * span.storeInContext(JContext.current()).makeCurrent() // store span in the context |
There was a problem hiding this comment.
makeCurrent() needs to be closed (2 instances in the doc example here)
| val jTracer = sdk.getTracer("tracer") | ||
| val span = jTracer.spanBuilder("test").startSpan() | ||
|
|
||
| span.storeInContext(JContext.current()).makeCurrent() |
There was a problem hiding this comment.
makeCurrent() needs to be closed here too
| ) { | ||
| override def toString: String = jOtel.toString | ||
|
|
||
| def withJContext[A](ctx: JContext)(fa: F[A]): F[A] = |
There was a problem hiding this comment.
do you think there should be a specialisation of this something like as follows?
final def withCurrentJContext[A](fa: F[A]): F[A] = withJContext(JContext.current())(fa)| def useJContextUnsafe[A](fa: JContext => A): F[A] = | ||
| Local[F, Context].ask.flatMap { ctx => | ||
| Async[F].fromTry { | ||
| val jContext = ctx.underlying | ||
| Using(jContext.makeCurrent())(_ => fa(jContext)) | ||
| } | ||
| } |
There was a problem hiding this comment.
given that this method calls makeCurrent() for you, should it just take a => A instead of a JContext => A?
(also, the parameter probably shouldn't be called fa)
There was a problem hiding this comment.
It looks to me like this method is intended for suspending a side-effect, but there is no delay/blocking/interruptible used here.
Which opens a bigger issue, that this API doesn't currently capture the different kinds of side-effects.
armanbilge
left a comment
There was a problem hiding this comment.
it addresses the same problem #214 tries to solve in a more limited but much simpler fashion, and is not held up for an unknown amount of time by reliance on an unreleased feature
I think this summarizes it really well. Essentially we have to make contextual counterparts of delay/blocking/interruptible. It also means it won't help with libraries such as fs2-grpc or http4s-netty (or doobie, does JDBC have otel integration too?). So unfortunately it's not very composable or orthogonal.
I'm not sure I would call it "simpler", at least for UX. I would agree that it's simpler to implement!
In an ideal world I'd say we should wait for #214 to do it right. But if there are real time needs, then that's motivation to move forward with this, but we should think about if/how we will migrate/deprecate these APIs in the future.
| def useJContextUnsafe[A](fa: JContext => A): F[A] = | ||
| Local[F, Context].ask.flatMap { ctx => | ||
| Async[F].fromTry { | ||
| val jContext = ctx.underlying | ||
| Using(jContext.makeCurrent())(_ => fa(jContext)) | ||
| } | ||
| } |
There was a problem hiding this comment.
It looks to me like this method is intended for suspending a side-effect, but there is no delay/blocking/interruptible used here.
Which opens a bigger issue, that this API doesn't currently capture the different kinds of side-effects.
|
@armanbilge @NthPortal thanks for the feedback! I tend to agree that the whole interop thing increases the complexity.
Definitely.
Even now, a library user can implement the same interop logic in their codebase. All necessary API is available. What if we go in a different direction? Rather than extending the API, I can make a documentation page that explains how to interop with Java-instrumented libraries. Then, a user can choose whether the side effect is blocking, interruptible, etc. It should be a sufficient solution. |
Yes, that sounds better 👍 |
The PR is based on the Discord discussion. It also slightly eases the pain of #202.
Motivation
Use case: you have a Spring server and a full set of Open Telemetry Java instrumentations.
You want to invoke
IOand it must capture the current tracing context:Use case: you want to invoke an instrumented Java library inside the IO. The instrumentation works only with thread-local context sharing: