-
Notifications
You must be signed in to change notification settings - Fork 36
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
Simplify interop between instrumented java libraries and otel4s #340
Conversation
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 have a couple of thoughts:
- these methods indirectly expose a decent amount of the API of
Local
; would it be reasonable forOtelJava
to explicitly expose theLocalContext
? and if so, would it be reasonable to extend that upwards toOtel4s
(probably promoting itsCtx
type 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makeCurrent()
needs to be closed here too
@@ -76,6 +137,17 @@ object OtelJava { | |||
traces.tracerProvider, | |||
) { | |||
override def toString: String = jOtel.toString | |||
|
|||
def withJContext[A](ctx: JContext)(fa: F[A]): F[A] = |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
IO
and 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: