Skip to content
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

Callback when SDK is fully initialized? #511

Closed
erawhctim opened this issue Mar 1, 2021 · 10 comments
Closed

Callback when SDK is fully initialized? #511

erawhctim opened this issue Mar 1, 2021 · 10 comments
Labels
enhancement New feature or request

Comments

@erawhctim
Copy link
Contributor

Are you requesting automatic instrumentation for a framework or library? Please describe.
N/A

Is your feature request related to a problem? Please describe.
I'm seeing a warning when trying to configure global SDK functionality immediately after calling Datadog.initialize(...).
Our setup looks like this:

class DatadogWrapper {

    fun init() {
        Datadog.initialize(/* Details omitted*/)

        Datadog.setVerbosity(if (BuildConfig.DEBUG) Log.DEBUG else Log.INFO)

        GlobalRum.registerIfAbsent(RumMonitor.Builder().build())
        GlobalTracer.registerIfAbsent(createTracer(...))
    }
}

Error log looks like this:

Datadog  E  Datadog has not been initialized.
         E  Please add the following code in your application's onCreate() method: 
         ...

Adding an artificial Thread.sleep() gives the SDK time to fully initialize so the secondary configuration calls don't trigger the error log (but that's obviously not ideal)

Describe the solution you'd like
A callback that's triggered when the SDK is initialized:

Datadog.initialize(some, params, here, listener)

where listener type is something like this:

interface SDKListener {
    fun onSDKInitialized()
}

Describe alternatives you've considered
Polling and constantly checking Datadog.isInitialized() (not ideal)

Additional context
N/A

@erawhctim erawhctim added the enhancement New feature or request label Mar 1, 2021
@erawhctim
Copy link
Contributor Author

I'm on the latest v1.8.0

@erawhctim
Copy link
Contributor Author

Upon further investigation, my setup looks very similar to what's in the sample app 🤔 So now I'm wondering if this is related to something else.

Is the error log an issue, or more of a warning? If this is just a warning, we may be able to ignore this.

@0xnm
Copy link
Contributor

0xnm commented Mar 2, 2021

Hello @erawhctim !

Thanks for taking the time to report the issue! Can you please check if you are using any Datadog SDK features (like DatadogInterceptor, for example, which may be active for HTTP call in Application#onCreate) before Datadog SDK initialization call? And if you have a possibility to show us a sample project where this issue is reproducible, this would be helpful. At the same time we are looking at the issue on our end.

@erawhctim
Copy link
Contributor Author

Your comment is actually quite helpful, I think that may be the exact issue I'm seeing (making an HTTP call before initializing the SDK). Let me do some digging and verify that.

I know we set up our OkHttpClient earlier in our dependency chain than when we call Datadog.initialize(), and we definitely set the DD interceptors on the client before SDK init as well.

@erawhctim
Copy link
Contributor Author

Alright so I've reworked the order of initialization on some of our dependencies and was able to get rid of the "not initialized" warning from my original post. Thank you!

I think I could still benefit from a callback/notification telling me when the SDK is fully initialized, due to a slightly related (but different) issue I'm seeing with the new v1.8.0 SDK. I'll log that as a separate bug ticket.

@mariusc83
Copy link
Collaborator

mariusc83 commented Mar 2, 2021

@erawhctim I am not sure why do you need that. The Datadog.initialize method is synchronous meaning that it once it is executed in the stack the SDK will be initialized. Your callback is the next line in your execution stack.

@orafaaraujo
Copy link

Hello,

We are using the Datadog.isInitialized() and GlobalRum.isRegistered() to not add the Interceptors until the client is ready. But I still have the error on my logcat.

We are about to add Datadog in our project and we are adding the library under a feature flag, in case we want to turn it off. So we skip the interceptors until the client is not ready. We only start the client once our feature flags are updated.

The implementation is also in a library module, not in the Application class but using Koin to provide the Context by androidContext() (I'm not sure if this is relevant, but sharing in any case).

Logs

// Skipping
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [false], Datadog.isInitialized() = [false]
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [false], Datadog.isInitialized() = [false]
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [false], Datadog.isInitialized() = [false]
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [false], Datadog.isInitialized() = [false]
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [false], Datadog.isInitialized() = [false]

// Start the client
I/DATADOG-LOCAL: Datadog.initialize()
I/DATADOG-LOCAL: GlobalRum.registerIfAbsent()
W/Datadog: A RUM event was detected, but no view is active. To track views automatically, try calling the Configuration.Builder.useViewTrackingStrategy() method.
    You can also track views manually using the RumMonitor.startView() and RumMonitor.stopView() methods.

// Client ready, add the Interceptors 
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [true], Datadog.isInitialized() = [true]
I/DATADOG-LOCAL: Adding: DatadogInterceptor(tracedHosts)
I/DATADOG-LOCAL: Adding: TracingInterceptor(tracedHosts)
I/DATADOG-LOCAL: Adding: DatadogEventListener.Factory()
I/DATADOG-LOCAL: GlobalRum.isRegistered() = [true], Datadog.isInitialized() = [true]
I/DATADOG-LOCAL: Adding: DatadogInterceptor(tracedHosts)
I/DATADOG-LOCAL: Adding: TracingInterceptor(tracedHosts)
I/DATADOG-LOCAL: Adding: DatadogEventListener.Factory()
W/Datadog: A RUM event was detected, but no view is active. To track views automatically, try calling the Configuration.Builder.useViewTrackingStrategy() method.
    You can also track views manually using the RumMonitor.startView() and RumMonitor.stopView() methods.

// Receiving the error
E/Datadog: Datadog has not been initialized.
    Please add the following code in your application's onCreate() method:
    val credentials = Credentials("<CLIENT_TOKEN>", "<ENVIRONMENT>", "<VARIANT>", "<APPLICATION_ID>")
    Datadog.initialize(context, credentials, configuration, trackingConsent);
W/Datadog: You added a TracingInterceptor to your OkHttpClient, but you didn't register any Tracer. We automatically created a local tracer for you.
E/Datadog: Datadog has not been initialized.
    Please add the following code in your application's onCreate() method:
    val credentials = Credentials("<CLIENT_TOKEN>", "<ENVIRONMENT>", "<VARIANT>", "<APPLICATION_ID>")
    Datadog.initialize(context, credentials, configuration, trackingConsent);
W/Datadog: You added a TracingInterceptor to your OkHttpClient, but you didn't register any Tracer. We automatically created a local tracer for you.

Besides the error, I can see the records on Datadog dashboard and everything looks fine.

Implemenation in a library module

override fun startProvider() {
    startLibrary()
    startMonitor()
}

private fun startLibrary() {
    if (Datadog.isInitialized()) return
    val credentials = getCredentials()
    val configuration = getConfiguration()
    val trackingConsent = TrackingConsent.GRANTED
    Datadog.initialize(context, credentials, configuration, trackingConsent)
    Datadog.setVerbosity(Log.VERBOSE)
}

private fun startMonitor() {
    if (GlobalRum.isRegistered()) return
    val rumMonitor = getRumMonitor()
    val userAgent = "omitted"
    GlobalRum.registerIfAbsent(rumMonitor)
    GlobalRum.addAttribute(ATTRIBUTE_USER_AGENT, userAgent)
}

private fun getCredentials(): Credentials {
    val clientToken = "omitted"
    val applicationId = "omitted"
    val environmentName = "omitted"
    val appVariantName = "omitted"
    return Credentials(clientToken, environmentName, appVariantName, applicationId)
}

private fun getConfiguration(): Configuration = Configuration
    .Builder(
        rumEnabled = true,
        crashReportsEnabled = true,
        logsEnabled = false,
        tracesEnabled = true,
    )
    .useSite(DatadogSite.EU1)
    .trackInteractions()
    .trackLongTasks()
    .useViewTrackingStrategy(ActivityViewTrackingStrategy(true))
    .trackBackgroundRumEvents(true)
    .build()

private fun getRumMonitor(): RumMonitor = RumMonitor
    .Builder()
    .sampleRumSessions(SAMPLING_RATE)
    .build()

Is there anything that I can do? Should we ignore this error?

(I'm also concerned about the A RUM event was detected, but no view is active. To track views automatically, try calling the Configuration.Builder.useViewTrackingStrategy() method. but this I can check in other issue).

Thank you in advance

@xgouchet
Copy link
Collaborator

Hi @orafaaraujo, and thanks for contacting us.

Our SDK comes with an Androidx Worker which is used to upload any pending data from previous sessions. The lifecycle of this worker is not something that we control, but we know it usually is woken up by the OS shortly after the application starts. Because you're delaying the call to Datadog.initialize(), when the worker wakes up it sees that the SDK is not initialized yet and can't proceed and display this warning.

In itself it's not necessarily an issue as this worker is mostly there to upload data when the app is killed, so while you're using the app you should be able to still upload data to Datadog, but this might have some unwanted side effect, especially in case of crashes. One advice is to persist the feature flag controlling whether or no to use Datadog to initialize it earlier.

@orafaaraujo
Copy link

Hey, @xgouchet
Thank you for the clarification.

Related to the "unwanted side effect, especially in case of crashes" I have two questions

  • The Worker uploads any pending data from previous sessions. Does this mean re-opening the app so the last data will be sent, even in the case of crashes?

  • Related to app start time metrics, is this somehow affected?

Once again, thank you for the quick reply. 🙏

@xgouchet
Copy link
Collaborator

The Worker uploads any pending data from previous sessions. Does this mean re-opening the app so the last data will be sent, even in the case of crashes?

Yes in case of a crash we ask the OS to wake up the UploadWorker to send the crash as soon as possible.

Related to app start time metrics, is this somehow affected?

It can be affected. The app start time is the duration between the beginning of the process and the moment a view is visible. If the SDK is initialized after the first view is resumed (visible to the end user) then the values detected will be off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants