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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[馃悶] GA4 sessions plummeted after migration of GTM to Partytown #583

Open
kyudorimj opened this issue Apr 12, 2024 · 10 comments
Open

[馃悶] GA4 sessions plummeted after migration of GTM to Partytown #583

kyudorimj opened this issue Apr 12, 2024 · 10 comments
Labels
bug Something isn't working

Comments

@kyudorimj
Copy link

kyudorimj commented Apr 12, 2024

Describe the bug

There's been a massive drop in GA4 sessions when checked on Analytics > Reports right after the migration of GTM to Parytown.

image

But it is not completely dead and only shows a massive decrease on session counts. Found out that this was caused by google collect? request not being sent when GTM is run through partytown. These requests was used by GA4 to track user session through session_start event. Each session can have multiple events associated with it:

image

Reproduction

Steps to reproduce

I'm using Angular, and here is how partytown and GTM is configured:

Partytown configuration script in the <head> tag inside index.html:

<script>
    partytown = {
      lib: "/~partytown/",
      forward: ["gtmInit", "dataLayer.push"],
    }
</script>
<script src="/~partytown/debug/partytown.js"></script>

GTM script inside index.html:

<script type="text/partytown">
  window.gtmInit = function(tagManagerId) {
    (function (w, d, s, l, i) {
      w[l] = w[l] || []; w[l].push({
        'gtm.start':
          new Date().getTime(), event: 'gtm.js'
      }); var f = d.getElementsByTagName(s)[0],
        j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
          'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
    })(window, document, 'script', 'dataLayer', tagManagerId);
  }
</script>

As you can see, I'm not using the gtag script from the documentation for Google Tag Manager (GTM). Instead, we are sending all tracking through GTM directly, because GTM can dynamically insert these scripts without needing gtag

Related open issue: #476

Browser Info

Chrome, Safari

Additional Information

No response

@kyudorimj kyudorimj added the bug Something isn't working label Apr 12, 2024
@replete
Copy link

replete commented Apr 13, 2024

I've come across this after testing out partytown myself with GTM. Even on a small hyper optimized website, and FYI: I found that the collect? GTM POST payload does not happen until over 5 seconds, even on subsequent visits, which for me means its not reliable for analytics like this.

EDIT 24/04/24: I've been investigating this and I've discovered this happens even normally, without partytown.
I have since learned more about sendBeacon() and using fetch() with keepalive:true (to prevent cancellation of request after window navigation and have observed GTM using both sendBeacon() and fetch() with or without partytown.

GTM.js is a complex script and at this point I'm going to leave this alone and return to this later. I think realistically if you are going to use partytown with GTM on an important site, you need a way to test if all the expected events are happening.

@kyudorimj
Copy link
Author

Indeed, @replete. I also discovered that this collect? requests was sent via sendBeacon while running through partytown, resulting in console errors during the request.

image
image

@hoebbelsB
Copy link
Contributor

@replete we ran into the same issue and figured out that we need to configure keepalive: false for the collect requests: #555

@indykoning
Copy link
Contributor

What is the best way to configure this to no longer be an issue?

{
    ...
    resolveSendBeaconRequestParameters: function (url, location) {
        if (url.pathname.includes('collect')) {
            return {keepalive: false};
        }

        return {};
    }
}

@kyudorimj
Copy link
Author

figured out that we need to configure keepalive: false for the collect requests: #555

great work @hoebbelsB , tho on my end there's no collect request at all when scripts were ran through partytown.

@kyudorimj
Copy link
Author

I've come across this after testing out partytown myself with GTM. Even on a small hyper optimized website, and FYI: I found that the collect? GTM POST payload does not happen until over 5 seconds, even on subsequent visits, which for me means its not reliable for analytics like this.

@replete , can you share us your GTM configuration?

@hoebbelsB
Copy link
Contributor

What is the best way to configure this to no longer be an issue?

{
    ...
    resolveSendBeaconRequestParameters: function (url, location) {
        if (url.pathname.includes('collect')) {
            return {keepalive: false};
        }

        return {};
    }
}

I think we have it configured as

resolveSendBeaconRequestParameters: function (url) {
    if (url.hostname.includes('google-analytics')) {
        return { keepalive: false };
    }
},

That they are not being sent at all is a different story, though. I'm also struggling with an integration right now. As of now i've figured out that the gtm.js event is not properly forwarded to the gtm script, suppressing its functionality.

For whatever reason, the dataLayer property gets swapped out in the gtm script.

When the gtm.js scripts gets initialized, the dataLayer still contains the gtm.js event, which is correct.

image

However, before initializing, the dataLayer property now does not contain the gtm.js event anymore, but instead some ids and the name dataLayer.

image

At least for our current state, this is the reason why nothing gets actually kicked off.

Btw. if I manually switch out the dataLayer to [{ event: 'gtm.js', 'gtm.start': new Date().getTime() }], it will properly kick off all events and download other third parties.

cc @gioboa

@hoebbelsB
Copy link
Contributor

@kyudorimj @gioboa I just figured out, that gtm internally accesses the window['global'] property.

image

This is not a known property to the worker as it seems, but it looks like it would be aware of the type to return. So what partytown then does is to create a new Window object for that accessor.

image

After this, the dataLayer object is reset. And it looks like this is happening for each nested gtm container.

I also found a workaround for this. We essentially just set the global property on window upfront in the index.html file.

<script type="text/partytown">
    (function() {
        window.global = document;
    })();
</script>

With this in place, all scripts are loaded and all gtm tags are working just fine.

Not sure if this is considered as bug, or intended behavior. Going to investigate a bit more about this. It might also be possible to introduce a GetterHook in order to achieve a similar behavior. For now I'm happy that things are working again 馃槗

@replete
Copy link

replete commented Apr 25, 2024

@hoebbelsB I was wondering about something similar last night but after some hours deep diving the GTM/partytown stack I decided GTM.js was very complicated and for now just switched partytown off. This is great info and I'll continue my own investigations at some point. I feel like I need to setup a temporary analytics endpoint and run alongside partytown so I can compare data on live for a few days before I can fully trust using partytown for analytics. It seems so very close, thanks for the update, I'll report back when I figure out more.

@kyudorimj Apologies for missing your comment, I also deleted a few comments to clean up the thread as I realized during my investigations I wasn't having quite the same issues. I'll come back to this but ultimately GTM is very complicated and I saw both fetch() and sendBeacon() being used, so there were multiple red herrings.

@kyudorimj
Copy link
Author

kyudorimj commented Apr 25, 2024

wow! was about to say the same @hoebbelsB . Thanks for your explanation, I don't have to do one. I think this can be considered as bug as it is not documented that it is how it works. On my end, altho similar to the workaround that you've done, I put the window['global'] assignment on my gtm scripts configuration in index.html directly. So now it looks like this:

<script type="text/partytown">
    window.initGoogleTagManager = function(tagManagerId) {
      (function (w, d, s, l, i) {
        <!-- Set global property of window for gtm to work -->
        w['global'] = d;
        w[l] = w[l] || []; w[l].push({
          'gtm.start':
            new Date().getTime(), event: 'gtm.js'
        }); var f = d.getElementsByTagName(s)[0],
          j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
            'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
      })(window, document, 'script', 'dataLayer', tagManagerId);
    }
  </script>

Also, on my end the resolveSendBeaconRequestParameters in parytown configuration looks like this:

   partytown = {
     ...,
     resolveSendBeaconRequestParameters: function (url) {
        return url.hostname.includes('analytics.google') ||
            url.hostname.includes('google-analytics')
            ? { keepalive: false }
            : {};
      },
    }

This is because, I noticed that the request hostname contains that string.

With all these in place, I was able to see the collect request on the Network tab now.

Screenshot 2024-04-25 at 21 33 48

I would still need to test more of it, because I still see some delays on the requests, but I'm happy as you are to see that it works now with that workaround.

If this is intended behavior, I think it should be documented in the GTM x Partytown docs. It is important to note that when implementing GTM within Partytown, the analytics should work. If this is the case, I can close this issue with that workaround, but only if it is added to the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants