Can I add an `ContinuationInterceptor` to KTor Se...
# ktor
a
Can I add an
ContinuationInterceptor
to KTor Server execution context? I am trying to add one just by doing
GlobalScope.embeddedServer(factory = Jetty, ... , parentCoroutineContext = MyContinuationInterceptor())
, but the interceptor never seems to trigger. I have done something similar with an Interceptor and
withContext(MyContinuationInterceptor()) { ... }
. That works, but thought I would "plug it in" at the "top" to see if I could do that. Is it at all possible?
c
This shouldn’t work properly, unfortunately. It’s not about ktor but continuation interceptors composition: https://github.com/Kotlin/kotlinx.coroutines/issues/2478
mostly because a coroutine dispatcher is a coroutine interceptor
Could you please explain your use-case? Why do you need it?
a
Hi @cy, thanks for the link. It looks spot on, and I'll look into it. My specific use case is that I am looking into the New Relic integration (found/using/changed code from @mp mainly) . The challenge is to "Link" the entrypoint (
JettyHandler
) to the rest of the call stack in a way that New Relic ties into the same Transaction. It kind of works now by intercepting the
ApplicationPipeline
and doing a
withContext(MyContinuationInterceptor()) { ... }
. This handles a
token.link()
call for each resume. But the first transaction (
JettyHandler
) is "logged" as a separate transaction from the ApplicationPipeline intercept. I was hoping I would be able to do something similar from the "top" level with the
parentCoroutineContext
. Any other approaches that might work? 🙂
m
Note that opentelemetry-java does the necessary context restoration by hooking in (via an agent) to empty coroutine hook functions provided for the debugger to use. Nasty, and not doable without an agent.
If you find a good solution I’d love to know about it. Tracing via coroutine context rather than thread locals (sans agent) would be a very useful building block.
c
I would try to put the required data into a context element + thread local element if required to tie it to a particular thread
m
the issue is that trace data is mutable 😕 ThreadContextElement works great for fixed things like a request id, but as soon as you start aggregating spans (thus mutable) I couldn’t figure out how to mesh that with the callbacks available via context element
(well, an issue maybe not the issue)
c
sometimes you may simply wrap mutable into a box but I know, sometimes it doesn’t work
but to be honest, there is not much you can do
a
Too bad. 😕 Hey @mp, do you have a way to get rid of the "duplicate" transactions in NR? Or just live with it? I thought about disabling the servlet auto-instrumentation, or maybe it would go away if the naming was the same? 🙂
m
We use the Netty engine, so we turned off NR’s auto-detection for Netty. The same probably applies to Jetty.
a
Thanks guys. 🙂 Just a last question @mp, how do you turn it off? 🙂 Netty pointer is fine, I just couldn't find it. I tried looking around the docs and Google, but very few pointers on modifying the config for it. And didn't find anything in newrelic.yaml either. 🙄
m
It was hard to find, let me dig that up
NR’s docs are not great.
Nor is NR’s product either but that’s another story 😉
under
class_transformer
section:
Copy code
# NR doesn't know what to do with ktor, so it just lumps everything into NettyDispatcher.
    # However, we use a separate ktor / NR integration.
    # Per <https://docs.newrelic.com/docs/agents/java-agent/async-instrumentation/disable-scala-netty-akka-play-2-instrumentation>
    com.newrelic.instrumentation.netty-3.4:
      enabled: false
    com.newrelic.instrumentation.netty-3.8:
      enabled: false
    com.newrelic.instrumentation.netty-4.0.0:
      enabled: false
    com.newrelic.instrumentation.netty-4.0.8:
      enabled: false
❤️ 1
a
Thanks 🙂
BTW: I agree NR leaves a lot to be wanted. But I still haven't found another alternative (easily available as SaaS) that actually gives that level of insights into DB/Network/Java methods automatically. If you know some please let me know. 🙂
m
Datadog’s instrumentation (currently in beta) is what I’ll be trying next.
The problem with any automagical thing is that it inevitably misses stuff, and NR essentially has nothing useful to offer other than what their agent can figure out automatically. Datadog at least is a perfectly reasonable place to write any other metrics you might capture.
The big feature that nobody offers (but DD is closest to) is allowing me to export a complete histogram. It’s used internally in DD’s agent but not publicly available yet.
a
So... The ugly solution... 😉 I copied out JettyKtorHandler (i know, i know, i know 😉 ) and modified it to get a token before
transaction.token();launch{...token.link()...}.invokeOnCompletion {token.expire()}
. I am also adding the token to the coroutineContext of launch so it's picked up by a copy of your code @mp that names the transaction correctly "inside" Ktor. So now the entire thing shows up as one transaction in NR with a proper naming. But... I am getting a "AsyncProcessing" part (basically a duplication of the time of the rest of the transaction) that I am not sure how to get rid of. You wouldn't have an idea @mp? I know it's hard to day, but I had to ask on the off chance you did. 🙂
m
Nothing comes to mind :/
a
Thanks 🙂 I'll go pester someone in the NR forums then. 😉
Just thought I'd pop back here to share that we found a 99% solution for New Relic and KTor if anyone is interested 🙂 https://medium.com/ztl-payment/observability-in-kotlin-with-ktor-and-new-relic-c562e56eb1e5