-
-
Notifications
You must be signed in to change notification settings - Fork 472
-
So there is following piece of code in SentryKtorClientPlugin.kt:
val parentSpan: ISpan? = if (forceScopes) scopes.getSpan() else { val currentScopes = Sentry.getCurrentScopes() if (Platform.isAndroid()) currentScopes.transaction else currentScopes.span }
Why if platform is android it always takes transaction as parent span? It makes it impossible to build span hierarchy the way I need/want. For example I have methods:
suspend fun doStuff() {
val sp = Sentry.getSpan().createChild("doStuff", "doing some stuff")
...
apiClient.getSomeDataFromServer() // internally does ktor http call
doSomeReusableStuff()
sp.finish()
}
suspend fun doSomeOtherStuff() {
val sp = Sentry.getSpan().createChild("doOtherStuff", "doing some other stuff")
...
apiClient.getOtherData() // internally does ktor http call
doSomeReusableStuff()
sp.finish()
}
suspend fun doSomeReusableStuff() {
val sp = Sentry.getSpan().createChild("doSomeReusableStuff", "doing reusable stuff")
..
apiClient.tiredOfMakingUpMethodNames() // internally does ktor http call
sp.finish()
}
and all http calls made in this code will appear "flat" - i.e. attached only to a top level transaction span instead of being nested. Or am I missing something?
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 2 comments
-
Hi @AngryGami, you're right to say that what you're trying to do doesn't work as you would expect.
In Android, we use something called global hub (aka scopes, with the hub -> scopes migration) mode.
This means that there's only a single set of scopes (hence, a single transaction and span) active at a given point in time, that are shared between threads.
This is in contrast with the behavior we have on JVM, where global hub mode is disabled by default, and each thread gets their own independent stack of scopes.
The reason we have these 2 modes is that, for Android, the thread local isolation doesn't really fit, as it's common to have operations that jump between threads.
With the way the auto-instrumentation is structured and our use of global hub mode, you get a correct span timeline, even though the hierarchy might be incorrect.
If we were to attach to the span though, instead of the transaction, the hierarchy would be completely broken, as you would grab whatever span happens to be active on the global hub (scopes) at the moment you create the child span for the HTTP request.
It should still be possible to do what you want, but at the moment it requires using APIs marked as internal. For example, you could create a new transaction using Sentry.startTransaction(transactionContext), constructing the transactionContext in a way that the transaction you start becomes a child of the current one. Then, set the currently started transaction as the active one on the scope, and at the end of the process restore the old one.
As you can understand this is not great currently, and requires a lot of manual work.
We're looking to improve this in future versions of the SDK, also considering that we're planning to move to a spans only API which will allow us to get rid of transactions, hopefully simplifying things.
I hope this helps.
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi @lcian
Thanks for thorough reply :)
As far as I understand (and from my experience), at least in modern Android+Kotlin development - main reason why operations might jump from thread to thread is usage of coroutines. SentryContext handles this (if used carefully) with almost no issues. Span that is returned by Sentry.getSpan() is like 90% of the time one that I'm expecting to see (other 10% are most likely my mistakes) so having at least an option to tell plugin to use it instead would be nice.
Beta Was this translation helpful? Give feedback.