DataDog/dd-sdk-flutter

Is it not possible to use data dog from flutter isolate ?

Den-creator opened this issue · 19 comments

Question

By calling DatadogSdk.instance.initialize in isolate I've receive such error:

[ERROR:flutter/runtime/dart_isolate.cc(1097)] Unhandled exception:
'package:flutter/src/services/platform_channel.dart': Failed assertion: line 542 pos 7: '_binaryMessenger != null || BindingBase.debugBindingType() != null': Cannot set the method call handler before the binary messenger has been initialized. This happens when you call setMethodCallHandler() before the WidgetsFlutterBinding has been initialized. You can fix this by either calling WidgetsFlutterBinding.ensureInitialized() before this or by passing a custom BinaryMessenger instance to MethodChannel().
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2 MethodChannel.setMethodCallHandler (package:flutter/src/services/platform_channel.dart:542:7)
#3 DatadogSdkMethodChannel.initialize (package:datadog_flutter_plugin/src/datadog_sdk_method_channel.dart:56:21)
#4 DatadogSdk.initialize (package:datadog_flutter_plugin/datadog_flutter_plugin.dart:168:21)

But WidgetsFlutterBinding.ensureInitialized() can not be called in isolate...

Yes this is limitation of the Datadog SDK at the moment, you can only use it from the main isolate at the moment.

If you have a need for this, please raise it as a feature request. It's definitely doable we just haven't had any requests for the functionality.

@fuzzybinary thank you for you reply !

@fuzzybinary - We need this for our impl. We've been stuck back on the 3rd party plugin but need to upgrade now due to the Apple Privacy changes.

How can we request this change?

For clarity - we run a single FlutterEngine with two isolates. On Android - one isolate runs within a foreground service and runs even when the UI isolate is terminated. On iOS it's similar - but the runtime model for iOS means both isolates are always running.

We primarily log from one isolate (background) but that isolate is always the second isolate to be established.

I'm working now to upgrade to datadog_flutter_plugin. I don't remember the exact behavior we had last time we tried - but I vaguely remember it being that DD was already configured and wouldn't allow the second isolate to spin up with configuration.

This works fine with the older, 3rd party plugin (datadog_flutter). Both isolates will log appropriately. I'll report back if I can't get that working with datadog_flutter_plugin

@dballance Yes, I'll try to raise it in priority.

Because Datadog acts as a singleton, you should still only configure once from your primary isolate, but I'll look into ways you can communicate with that singleton from multiple isolates.

An update as I am working through the upgrade.

Atleast on Android - as long as I call DatadogSdk.instance.initialize in both isolates, I get logs from both. No obvious errors and logs do not indicate any DD SDK configuration errors.

This is required to ensure we can call DatadogSdk.instance.logs?.createLogger in both isolates. Should I be using the attachToExisting in the secondary isolate? We work similarly to the "app in app" in terms of how we're bootstrapped.

attachToExisting does seem to work for us. Will need to check on iOS to be sure (I think this is where the rub was last time). Hopefully I can get to that this afternoon and report back.

@fuzzybinary - finally got through this today. It seems attachToExisting - since we're just using logging - will work for us as it is.

One enhancement that would be nice - is wrapping any awaits for pending initialization into the the attachToExisting call. There is a race condition where a call to initialize may be in flight from one isolate, but not complete (somehow?), when the second isolate calls attachToExisting.

In my current impl to get this working, Isolate A calls initialize then starts Isolate B, which immediately tries to get the DD SDK running so it can attach logs. In this case, Isolate A awaits completion of the initialize call before it proceeds to "starting" Isolate B. Aside: Isolate A also starts Isolate B not via spawn but via a platform call to a host FlutterEngineGroup to start the FlutterEngine that runs Isolate B.

This seemed to work fine on Android - but on iOS the first call to attachToExisting from Isolate B always seemed to fail. I added in some recursion - call attachToExisting and if instance.logs is null, call it again until it returns an instance - and it seems to always attach on the second call here.

Could purely be some Swift shared resource thing, but would be nice to either return an obvious failure (throw or return a result that could be used here to understand that attachToExisting didn't find an "existing") AND potentially have the attachToExisting call wait on any pending initialize calls before returning.

May not be possible - but wanted to pass it along in case it was useful.

Thank you for your effort with datadog_flutter_plugin!

Hey @dballance

Thanks for all the excellent info. My guess here as to the source of your race condition is the wait on the platform channel to perform its operations for initialization. I'm not sure why attachToExisting returns null on that first try, but I'll look into it.

Likely the way I'll approach attaching from a new isolate and / or waiting for initialization is with a separate call, as attachToExisting is meant more for hybrid apps than for separate isolates. I'm thinking something like the following:

void startIsolateasync  {
  await Datadog.waitUntilInitialized(timeout: 1.0);
  var datadog = Datadog.attachIsolate()
}

This way, folks who don't immediately start all isolates don't have to perform the wait.

Let me know if you have other issues, and I'll let you know when I have a cleaner interface for this.

We've pushed into production after migrating with attachToExisting on our end and happily seeing logs from both iOS and Android across multiple isolates.

Know it's not ideal - but is working for us. Might still be issues in RUM or other parts of the SDK, but atleast for logging we're 🎉.

🍻

Cool. Thank you !