sentry-java icon indicating copy to clipboard operation
sentry-java copied to clipboard

Attach custom child spans to the cold app start transaction

Open zhukic opened this issue 1 year ago • 8 comments

Problem Statement

Hi!

I use the performance v2 feature, and as the next step, I'd like to measure specific operations time during the application.load operation, for example, and enrich the transaction with more detailed spans. I haven't found the proper way to do this at the moment. Could you please suggest if there is some workaround to do that using the current API(custom callbacks or event processor?)

Expected behaviour: possibility to add child spans to the existing spans(application.load, activity.load, etc.) during cold app start.

SDK version - 7.9.0

Thank you!

Solution Brainstorm

No response

zhukic avatar Jul 16 '24 12:07 zhukic

I also have some "Missing instrumentation" spans. I know which operations causes them and I'd like to have possibility to mark them explicitly as the child spans.

image

zhukic avatar Jul 16 '24 12:07 zhukic

@zhukic thanks for reaching out! Yes, that's something we've been considering for a while, let me discuss this internally!

In the meantime you could utilize EventProcessors to add your custom spans, here's a snippet: Beware these are internal APIs, so they might not be stable across releases.

SentryAndroid.init(this) { options ->
            options.apply {
                dsn = ...
                addEventProcessor(object : EventProcessor {
                    @Suppress("UnstableApiUsage")
                    override fun process(
                        transaction: SentryTransaction,
                        hint: Hint
                    ): SentryTransaction {
                        transaction.contexts.trace?.traceId?.let { traceId ->
                            transaction.spans
                                .firstOrNull { it?.op.equals("app.start.cold") }
                                ?.let { appStartSpan ->
                                    val parentSpanId = appStartSpan.spanId

                                    val startTimeStamp = 0.0 // unix time in seconds
                                    val endTimeStamp = 0.0 // unix time in seconds
                                    val op = "component.op"
                                    val description = "description"

                                    transaction.spans.add(
                                        SentrySpan(
                                            startTimeStamp,
                                            endTimeStamp,
                                            traceId,
                                            SpanId(),
                                            parentSpanId,
                                            op,
                                            description,
                                            SpanStatus.OK,
                                            null,
                                            emptyMap(),
                                            emptyMap(),
                                            emptyMap(),
                                            emptyMap(),
                                        )
                                    )
                                }
                        }
                        return transaction
                    }
                })
            }
        }

markushi avatar Jul 17 '24 10:07 markushi

@zhukic could you describe your use-case a bit more in detail? Which kind of operations do you want to measure? Is it mainly about the execution of Application.onCreate()?

markushi avatar Jul 17 '24 13:07 markushi

@markushi Thank you for the answers!

I'll try the approach with the EventProcessor a bit later.

could you describe your use-case a bit more in detail? Which kind of operations do you want to measure? Is it mainly about the execution of Application.onCreate()?

During the Application.onCreate() I initialize lots of my app's components and it takes some time. I'd like to have a possibility to measure all the initializations separately and attach them as the child spans to the application.load span.

The second use case is described here. It's my code which runs on the UI thread, and currently, it's marked as "Missing instrumentation". I'd also like to have a possibility to attach it as a child span. I guess, this can be also solved by a custom EventProcessor.

zhukic avatar Jul 18 '24 09:07 zhukic

@zhukic thanks for the details, that's very helpful! On a side note - would you be interested if we provided auto-instrumentation for those components that get initialized as part of Application.onCreate? We have some ideas on how to to do that, although it may become a bit noisy, but we can think of some configuration/ignorelist.

romtsn avatar Jul 18 '24 09:07 romtsn

@romtsn

would you be interested if we provided auto-instrumentation for those components that get initialized as part of Application.onCreate

Yes, I would!

We have some ideas on how to to do that, although it may become a bit noisy

What do you mean?

I'd expect that I can configure myself which parts of code to make measurable, something like this:

override fun onCreate() {
    super.onCreate()

    //some code

    initialize1()
    initialize2()

    //some code
}

@ApplicationOnCreateSpan(spanOperation = "initialization1")
private fun initialize1() {
    //
}

@ApplicationOnCreateSpan(spanOperation = "initialization2")
private fun initialize2() {
    //
}

and application.load span would have two child spans: initialization1, initialization2

zhukic avatar Jul 18 '24 12:07 zhukic

@markushi @romtsn Hi!

I've tried the solution with the custom EventProcessor, and looks like it works! The only thing that concerns me is that there are no application.load and activity.load spans that, as I see, get attached in PerformanceAndroidEventProcessor which is called after my custom processor.

I see that there is also BeforeSendTransactionCallback which contains all the spans but I'm not sure if it's ok to modify the transaction on this stage.

So the UI looks like this for the spans which are created from Application.onCreate:

Image

But it's good enough for now!

If you have some suggestions - would be glad to hear.

Thank you for help!

zhukic avatar Jul 19 '24 09:07 zhukic

@zhukic Glad it's working for you! Actually it should be safer to use BeforeSendTransactionCallback instead of a custom EventProcessor, as the BeforeSendTransactionCallback is executed after all processors.

markushi avatar Jul 29 '24 05:07 markushi