DataDog / dd-sdk-flutter

Flutter bindings and tools for utilizing Datadog Mobile SDKs
Apache License 2.0
41 stars 40 forks source link

[RUM][Mobile + Web] Custom attribute's value is being updated #564

Open toussam opened 4 months ago

toussam commented 4 months ago

Hi 👋

Describe what happened We are tracking views and providing additional information using custom attributes. Among them, we have attributes provided for specific views and others who are "general" custom attributes being applied on all views' tracking using the rum's addAttribute method.

We noticed that if we set up or update a "general" custom attribute after tracking a view then the said custom attribute's value linked to this last event will be updated.

Steps to reproduce the issue:

Describe what you expected: The tracked view event's should contain the custom attribute with the initial value and not the updated one.

Additional context

Thanks in advance for your investigation and future response. 🙏

Best regards,

fuzzybinary commented 4 months ago

Hi @toussam,

So I can be clear on what you're expecting -- for a global attribute added through DatadogSdk.instance.rum?.addAttribute, you're expecting a view to have the value of the attribute when it started, even if that attribute is changed during the life of the view?

toussam commented 4 months ago

Hi @fuzzybinary,

This is indeed the behavior that I was expecting / trying to reach.

Now that you're referring the view's lifecyle, it does make sense. So I presume that the only way to do so would be to set up the custom method for each event using the startView method's third parameter (attributes), am I right?

If so, I did tried this solution and it worked for Android, but sadly not for iOS. I just tested back and I can confirm you that the custom attribute's value is still being updated for the view event.

Also, using this solution, I noticed the #565 issue for the Web.

fuzzybinary commented 4 months ago

Yes, the behavior you were seeing was expected. Attributes are considered a stateful property of the view, so changing them updates the property in the view. However, we do capture the current state of the global attributes at certain points of the view, it's just RUM will always show you its last known value.

I'm surprised that using the attributes parameter of startView didn't work for iOS -- can you supply some code that reproduces the issue you're seeing? iOS and Android should behave the same.

How you approach this depends on what you're trying to track. Can you give me more information about your use case?

fuzzybinary commented 4 months ago

@toussam Following up on this -- The original bug is as designed, but I'd still like to make sure startView is working as expected on iOS. Do you have an example of it not working as expected?

toussam commented 4 months ago

Hi @fuzzybinary,

Sorry for the late response.

I thought I could provide a sample sooner, but I had a busy week.

Will test things back and try to provide you something ASAP.

toussam commented 3 months ago

Hi @fuzzybinary,

Again, sorry the late response.

Just tested back and can confirm to you the described behavior when not using "global" attributes and so by providing them "manually" when calling the "startView" RUM's method.

Here are a few lines to reproduce the said issue on iOS platform:

  int _index = 0;

  Future<void> track(String eventName) {
    // Uncomment the below line and comment the class member to test the "local variable case"
    //int index = 0;

    final Map<String, dynamic> properties = {"test": "test_{$_index}"};
    index++;

    try {
      _datadog.rum?.startView(eventName, null, properties);
    } on Exception catch (ex) {
      debugPrint(ex.toString());
    }
  }

Steps to follow:

In our case, we don't update the custom attributes right away like for the index. Indeed, before pushing a new page into our navigating system, we have most of the time some "behind work" in which we will update some data or whatever. Real life examples would be signing in or updating the user's email for example.

Observed behaviors:

when using the class property, the provided value is "test_0" for the first page (checked using a breakpoint), the observed value on DG is "test_0" in a first time (if you're quick enough) then quickly become "test_1" after refreshing DG dashboard.

when using the local property, the provided and observed value will (obviously!) be the same aka "test_0".

when using the class property AND when using the local property, the provided and observed value will be the same aka "test_0".

I understand that global attributes are considered stateful properties. I don't know if it's also the case for the one provided manually for each tracking events, but our needs is to track some data at T time (aka tracking time) and it's a bit weird to not have the same behavior on both platforms so again not sure if there is a real issue here.

Let me know if you need more information or anything.

Best regards,

fuzzybinary commented 3 months ago

Hi @toussam,

Thanks for doing the legwork here. I'll take a look into it. If the platforms are not behaving the same that is definitely a bug I want to look into.

I'll see if I can figure out what's going on and hopefully have an answer to you soon.

Thanks again for the reporting!

fuzzybinary commented 3 months ago

@toussam Is it possible you're sending multiple startView events with the same name? From an initial investigation, I think it's possible that iOS isn't reporting the new view if it has the same name, and instead updates the properties on the original view. On Android it creates two views.

I'll investigate more why iOS isn't behaving the same way here, looking to see if you're making those duplicate calls might give you a workaround. Alternately, calling stopView manually may give you the same behavior between the two platforms.

toussam commented 3 months ago

Hi @fuzzybinary,

It's technically possible, yes. As explained into my previous message we're tracking our views therefore if you go back, you will have two events with the same name, but to be honest with you when I tested back I did so following a standard flow from our app in which I didn't do any backward navigation so when observing my issue, each events had a different name.

Thank you for your investigation and response! 🙏

EDIT/ Just tested calling the "stopView" method right after the "startView" one as suggested and it's indeed giving the same behavior as for the Android platform. 👍