-
Notifications
You must be signed in to change notification settings - Fork 55
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
Propagate OTEL context between ZIO and non-ZIO code #580
Conversation
9c6006f
to
fc0882a
Compare
fc0882a
to
fd02eca
Compare
import io.opentelemetry.context.Context | ||
import zio._ | ||
|
||
import java.util.concurrent.ConcurrentHashMap |
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 guess it can be replaced with https://zio.dev/reference/sync/concurrentmap. As a consequence, you will have Supervisor[UIO[Unit]]
and @nowarn
annotation might be omitted.
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'm afraid we'll have to stick to the regular ConcurrentHashMap
because all callbacks in Supervisor
are unsafe and return Unit
. Unsafe side effects have to be executed within those callbacks for context to be properly propagated into non-ZIO code.
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.
@nowarn
basically tells us that we are throwing away Scope
that we get when we call Context.makeCurrent()
. Ideally we shouldn't do this. I saw that other tracing libraries (like Datadog's tracing SDK) use stacks of scopes. So potentially we could change storage
to be of type ConcurrentHashMap[FiberId, Stack[Scope]]
. But this is kind of involved and apparently this all works even when we do throw scopes away.
@dmytr Thanks for your contribution! Sorry for the delay, I honestly need some time to process this PR. |
Thanks again, @dmytr, for your contribution! It looks good to me. |
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.
Very nice contribution. Given the interface surface area change is very small, I'd advocate for merging and releasing soon to make it easier to get more feedback. In general, I think the approach is the right one -- configure the zio runtime appropriately and context propagates as expected into and out-of the zio runtime.
@dmytr Hey! Can we merge it? |
@grouzen yes, please. I don't have access to do this. |
I've updated my test harness to use the published snapshot version: dmytr/zio-otel-instrumentation#2 |
I'll cut a release ASAP. |
It was reverted to the old style by accident (I think?) in zio#580
It was reverted to the old style by accident (I think?) in zio#580
Motivation
At the moment OTEL context is not propagated between ZIO and non-ZIO code. Of course, there are functions from
scopedEffect
family provided byTracing
, but they expect an unsafe size-effecting code as their argument.In case if you need to propagate context into libraries that wrap non-ZIO code into ZIO, but don't use tracing - there is no guarantee that underlying non-ZIO code will have access to the correct context.
And in cases when non-ZIO code invokes ZIO code (for example in
zhttp
, which starts fibers from Netty code) context is not (correctly) propagated as well.This change makes it possible to use automatic instrumentation provided by OpenTelemetry JVM agent.
Related: #561
How this works
This solution uses a special ZIO supervisor that takes a snapshot of the current OpenTelemetry context before a new ZIO fiber is started and uses it as a parent context in the scope of that fiber.
Each time a fiber is suspended, supervisor takes a fresh snapshot of the current context and then restores the root context. When fiber is resumed, it restores the last context snapshot for that fiber.
When fiber is completed, supervisor restores the root context.
Probably, this is not the most correct or efficient implementation, but it does seem to work.
There is an end-to-end setup that uses this kind of supervisor here: https://github.com/dmytr/zio-otel-instrumentation.