https://developers.google.com/analytics/devguides/collection/ga4/set-up-ecommerce
Guerilla
Adding gtag.js/individual and overall SITE + NATS
{literal}
<!--NSWhiteLabels Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZGSEPJTQ7G"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZGSEPJTQ7G');
</script>
<!-- Guerilla.com Google tag (gtag.js) -->
<script>
window.dataLayer = window.dataLayer || {};
function gtag(){dataLayer.push(arguments);}
// Environment-specific configuration
var hostname = window.location.hostname.toLowerCase().trim();
var GA_MEASUREMENT_ID; // Declare variable without initial value
// Define your environment domains and corresponding GA Measurement IDs
if(hostname === "dev.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Development environment ID
} else if(hostname === "stage.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Staging environment ID
} else if(hostname === "www-guerillaporn.nakedsword.org") {
GA_MEASUREMENT_ID = 'G-85Z5DGLTLN'; // QA environment ID
} else if(hostname === "shawns-machine.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW';
} else if(hostname === "guerillaporn.com" || hostname === "join.guerillaporn.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // Production environment ID for both 'guerillaporn.com' and 'join.guerillaporn.com'
} else {
console.log("Environment not recognized or is local. GA not initialized.");
GA_MEASUREMENT_ID = undefined; // GA won't be initialized for unknown or local environments
}
// Dynamically set the script src to load gtag.js with the correct GA_MEASUREMENT_ID
if (GA_MEASUREMENT_ID) {
var gtagScript = document.createElement('script');
gtagScript.async = true;
gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
document.head.appendChild(gtagScript);
gtagScript.onload = function() {
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
};
}
</script>
Adding GTM SITE
<!-- Google Tag Manager -->
<script id="gtm_script" data-nonce="NONCE_VALUE" nonce="NONCE_VALUE">
(function(w, d, s, l, i) {
w{l} = w{l} || {};
w{l}.push({
'gtm.start': new Date().getTime(),
event: 'gtm.js', // GTM default event for container load
whitelabel: {
gtm_auth: '%REACT_APP_GTM_AUTH%',
gtm_preview: '%REACT_APP_GTM_PREVIEW%'
}
});
// initial page load
w{l}.push({
'event': 'nsWhiteLabelLoad' // Custom event for SPA initial load
});
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;
var n = d.querySelector('{nonce}');
if (n) j.setAttribute('nonce', n.nonce || n.getAttribute('nonce'));
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', '%REACT_APP_GTM_ID%');
</script>
<!-- End Google Tag Manager -->
GTM NATS PreJoin
{literal}
<!-- Google Tag Manager -->
<script>(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','{/literal}{#tagManagerID#}{literal}');</script>
<!-- End Google Tag Manager -->
{/literal}
Approval Page NATS
{literal}
<script>
document.addEventListener('DOMContentLoaded', function() {
let hashedEmail = localStorage.getItem('user_id');
let custom10hashedEmail = '{/literal}{$member.custom10}{literal}'; // Ensure the Smarty variable is treated as a string
if (hashedEmail === custom10hashedEmail) {
window.dataLayer = window.dataLayer || {};
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
gtag('event', 'purchase', {
"user_id": hashedEmail,
"nats_id": '{/literal}{$member.memberid}{literal}';
"currency": '{/literal}{$transaction.biller_currency|addslashes}{literal}',
"transaction_id": '{/literal}{$member.memberidx|addslashes}{literal}',
"value": {/literal}{$member.spent/100}{literal},
"items": {
{
"item_id": '{/literal}{$member.optionid}{literal}',
"item_name": '{/literal}{$option.details.name|addslashes}{literal}',
"affiliation": '{/literal}{$member.memberid}{literal}',
"coupon": "",
"discount": 0,
"item_brand": '{/literal}{$site.site|addslashes}{literal}',
"item_category": "Membership",
"price": {/literal}{$member.spent/100}{literal},
"quantity": 1
}
}
});
localStorage.setItem('nats_id', '{/literal}{$member.memberid}{literal}'); // Update local storage value of nats_id
}
});
</script>
{/literal}
PreJoin NATS
<script src="{#joinURL#}{#natsSiteNamesFile#}"></script>
{literal}
<!-- ANALYTICS -->
<script>
// Define the decodeNatsMP function
function decodeNatsSMP() {
var data = document.getElementById('nats').value;
var chars = data.length;
var add = 4 - (chars % 4);
if (add < 4) {
while (add) {
data += '=';
add -= 1;
}
}
var decodedData = atob(data);
var splitData = decodedData.split('.');
var labels = {'AffiliateID', 'ProgramID', 'SiteID', 'TourID', 'CampaignID', 'AdtoolID', 'AdToolSub1ID', 'AdToolSub2ID'};
//siteNames load here from natsSiteNames.js file included in page
var decodedValues = {};
splitData.forEach((value, index) => {
if (index < labels.length) {
decodedValues{`nats_${labels{index}}`} = value;
if (labels{index} === 'SiteID') {
decodedValues{"nats_SiteName"} = siteNames{value} || 'Unknown Site';
}
}
});
return decodedValues;
}
let precomputedNatsValues = {}; // Global variable to store precomputed values
document.addEventListener('DOMContentLoaded', function() {
// Precompute the natsValues as soon as the page loads
precomputedNatsValues = decodeNatsSMP();
//check preconfig values
console.log('Precomputed nats values: ' + JSON.stringify(precomputedNatsValues));
let isSubmitting = false;
document.getElementById('natsPreJoinForm').addEventListener('submit', async function(event) {
if (isSubmitting) return;
isSubmitting = true;
event.preventDefault();
// Async function to hash email
async function hashEmailAndStore(email) {
const msgUint8 = new TextEncoder().encode(email.toLowerCase());
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
localStorage.setItem('user_id', hashHex); // Store hashed email as user_id in local storage
return hashHex;
}
try {
const email = document.getElementById('usernameemail').value;
const hashedEmail = await hashEmailAndStore(email);
document.getElementById('hashedEmailInput').value = hashedEmail;
// document.getElementById('hashedEmailInputRkt').value = hashedEmail;
document.getElementById('waiter').style.visibility = 'visible';
// Use the precomputed nats values directly
console.log('Using precomputed nats values: ' + JSON.stringify(precomputedNatsValues));
const selectedOption = document.querySelector('.option.pressed-button');
if (!selectedOption) {
throw new Error('No option selected');
}
const optionId = selectedOption.id.split('-'){1};
const priceCurrency = selectedOption.querySelector('.price-currency')?.textContent || '$';
const priceWhole = selectedOption.querySelector('.price-init')?.textContent || '0';
const priceCents = selectedOption.querySelector('.price-cents')?.textContent || '.00';
const signUpValue = parseFloat(priceWhole + priceCents).toFixed(2);
const eventDetails = {
"send_to": "{/literal}{#gtagConfig#}{literal}",
"user_id": hashedEmail,
"method": "email",
"event_category": "registration",
"event_action": optionId,
"event_label": document.referrer || "Direct/No Referrer",
"event_currency": priceCurrency,
"event_value": signUpValue,
...precomputedNatsValues
};
gtag("event", "sign_up", eventDetails);
console.log("Event sent with dynamic values: ", eventDetails);
event.target.submit(); // Programmatically submit the form
} catch (error) {
console.error("Error:", error);
} finally {
document.getElementById('waiter').style.visibility = 'visible';
}
});
});
</script>
{/literal}
Initial Config + Page View
To integrate the individual GA4 measurement tag alongside the overall tag and ensure navigation changes (page views) are sent to both GA4 properties, especially within a React application that includes PHP pages, you can expand upon the initial setup. This involves initializing both GA4 properties in your index.html
and ensuring that navigation events are tracked and sent to both properties.
Step 1: Initializing Both GA4 Properties in index.html
Adjust your initial GA4 script in the index.html
to include both the overall and individual GA4 measurement IDs. You would initialize the GA4 environment with both IDs, ensuring that the base GA tracking script (gtag.js
) is included once but configured for both IDs.
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID_PLACEHOLDER"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
// Initialize both GA4 properties
gtag('config', '%REACT_APP_UNIVERSAL_GA_ID%');
gtag('config', '%REACT_APP_ENV_GA_MEASUREMENT_ID%');
// of sending initial page view another wayƒ
// gtag('config', 'YOUR_OVERALL_GA_MEASUREMENT_ID', {'send_page_view': false});
//gtag('config', 'YOUR_INDIVIDUAL_GA_MEASUREMENT_ID', {'send_page_view': false});
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=YOUR_OVERALL_GA_MEASUREMENT_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
// Initialize both the overall and individual GA4 properties
gtag('config', 'YOUR_OVERALL_GA_MEASUREMENT_ID', {'send_page_view': false});
gtag('config', 'YOUR_INDIVIDUAL_GA_MEASUREMENT_ID', {'send_page_view': false});
</script>
In this adjusted script:
'send_page_view': false
is specified to manually control when page view events are sent, allowing for more accurate tracking in single-page applications (SPAs) and specific PHP pages.
Step 2: Dynamically Sending Page View Events
Next, include a script to dynamically send page view events to both GA4 properties upon navigation changes. This script handles the initial page load and subsequent navigations, which is crucial for SPAs like those built with React.
<script>
// Function to send page view events to both GA4 properties
function sendPageView() {
const pagePath = window.location.pathname + window.location.search;
gtag('event', 'page_view', {
page_path: pagePath,
send_to: ['YOUR_OVERALL_GA_MEASUREMENT_ID', 'YOUR_INDIVIDUAL_GA_MEASUREMENT_ID']
});
}
// Send a page view event for the initial page load
sendPageView();
// Listen for history changes to handle SPA navigation
window.onpopstate = sendPageView;
// For React apps, consider invoking sendPageView() on route changes within the app
</script>
Step 3: Integrating with React
In a React application, particularly SPAs using React Router or Reach Router, you'll need to ensure sendPageView()
is called on route changes. This can be integrated using React's effect hooks.
For example, with React Router:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function usePageViews() {
let location = useLocation();
useEffect(() => {
// Defined globally in your index.html
sendPageView();
}, [location]);
}
Call usePageViews
in your app's main component to ensure sendPageView
is called on every route change.
Step 4: PHP Page Integration
For PHP pages, the initial script from index.html
(including both GA4 initialization and the sendPageView
function) should be included in the PHP template. This ensures that page views on PHP pages are also tracked.
Final Note
This setup provides a flexible foundation for tracking both overall and specific navigations across your site, accommodating both the SPA nature of React apps and traditional PHP pages. Adjust the sendPageView
function as necessary to include additional data or handle specific tracking needs.
Here's an example script you can place at the end of the <body>
in your index.html
that works for both types of sites:
<script>
window.addEventListener('load', function() {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({'event': 'site-loaded'});
});
</script>
Step 3: Create a Custom Trigger in GTM
- Open GTM: Navigate to your GTM dashboard.
- Create a New Trigger: Go to "Triggers" > "New".
- Trigger Configuration: Select "Custom Event" as the trigger type.
- Event Name: Set the event name to "site-loaded", matching the event pushed to the data layer.
- Trigger Fires On: Choose "All Custom Events" with the event name "site-loaded".
Step 4: Set Up a GA4 Event Tag Triggered by "site-loaded"
- Create a New Tag: In GTM, go to "Tags" > "New".
- Tag Configuration: Select "Google Analytics: GA4 Event".
- Configuration Tag: Choose your existing GA4 Configuration Tag (you should have this set up from when you configured GA4 within GTM).
- Event Name: Enter an appropriate GA4 event name (this can be "site_loaded" or any name that follows GA4 naming conventions and makes sense for your tracking purposes).
- Triggering: Select the "site-loaded" trigger you created in the previous step.
Step 5: Testing and Publishing
- Preview and Test: Use GTM's preview mode to test your setup. Ensure that the "site-loaded" event fires as expected when the site loads.
- Publish: Once you've verified that everything is working correctly, publish your changes in GTM.
By following these steps, you create a specific "site-loaded" event that fires once when your site loads, kickstarting GTM and GA4 tracking. This setup ensures you have a consistent tracking event across different technologies and platforms.
AnalyticsPlan.md.zip
OK – I hear Gary – but as he expressed – even with the segmenting (which does NOT always do what you think it will) there is still some manipulation of the data that has to occur AFTER everything gets up to Analytics. To make the reports – you also have to be aware of what segments you need to use and apply them correctly. Past experience has shown me – no matter how easy (premade reports I made) – or my attempts to break down what event paramaters to use to get the metrics they want in a chart – it just doesn't translate. Our users want what they want – the way they want it. Adding ALL sites together – I feel willl lesson the trust in the data we will get because they won't see what they expect. While my origijnal idea of tracking all the price points was indeed asinine – BUT Andy didn't ask me to stop becuase it wouldn't work.. He asked me to stop because it would have and they were not ready for another way for the "world" to see our $.
Additionally – we are a SPA (single page application) – which means to track all "page" views – we work with changes in the history. GTM makes that very simple to do – and allows a great ability to change what you are reading. It's still possible to get page views without GTM in a SPA – but requires a component to be built that mimics what analytics gets from normal sites. Also not a big deal – UNTIL – you want to change that fundamament metirc. Then that's a deploy etc. It makes sense to keep using GTM and to use it for all its' worth in that case – and really I don't agree with his theory that using ONLY the gtag.js is superior to GTM. GTM is a little asshole – but deservidly so – with it you can do A LOT more immediately without having to change anything in the applciation it self.
I wasn't getting login events for a nubmer of reasons (they work now) – one of the solutions I tried was to really break out the sites from eachother – we had an old UA (universal analytics call) but the gtag.js for GA4 was brannd new. GTM was already there – so adding gtag.js is new – I also wanted to avoid another tango with DevOps over these silly values – so was looking for a way to swap around the streamids without having to add it into their config.
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Let's get beta - qa - out of prod metrics for good -
var hostname = window.location.hostname.toLowerCase().trim();
var GA_MEASUREMENT_ID;
// use hostname to break out streamids - clean simple easy -
if(hostname === "dev.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Development environment ID
} else if(hostname === "stage.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Staging environment ID
} else if(hostname === "www-guerillaporn.nakedsword.org") {
GA_MEASUREMENT_ID = 'G-85Z5DGLTLN'; // QA environment ID
} else if(hostname === "shawns-machine.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // need this for testing - remove for prod.
} else if(hostname === "guerillaporn.com" || hostname === "join.guerillaporn.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // have to keep it going on nats - rocketgate has static tag but that is ok -
} else {
console.log("Check environment settings - or - not streamID present");
GA_MEASUREMENT_ID = undefined; //stop if wrong / blank
}
// set sreamID
GA_MEASUREMENT_ID
if (GA_MEASUREMENT_ID) {
var gtagScript = document.createElement('script');
gtagScript.async = true;
gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
document.head.appendChild(gtagScript);
gtagScript.onload = function() {
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
};
}
</script>
Now the GTM set up is fairly extensive – google does not have a way to "copy" coifigs around (so oculd be error prone – reentering data inot eac property. BUT you can use a lookup table and base it off hostname (like above) and use the same GTM contianer for all enviroments. It automatcally segments the data BEFORE analytics. so it won't end up everywhere.
here's an overview of what goes into tracking – and why I would like to only manage one instance of this:
To get to Gary's idea.
So if getting the environments separated out works – why not just expand it – AND – add a OVERALL stream ID – that collects all data from all sites – it will give us everything we want – without polluting the individual sites.
<!-- MASTER OVERALL Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MASTER123"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-MASTER123);
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Environment-specific configuration
var hostname = window.location.hostname.toLowerCase().trim();
var GA_MEASUREMENT_ID;
if(hostname === "dev.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Development environment ID
} else if(hostname === "stage.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Staging environment ID
} else if(hostname === "www-guerillaporn.nakedsword.org") {
GA_MEASUREMENT_ID = 'G-85Z5DGLTLN'; // QA environment ID
} else if(hostname === "shawns-machine.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW';
} else if(hostname === "anotherwhitelabel1.com") {
GA_MEASUREMENT_ID = 'G-5538PPXXWW';
} else if(hostname === "anotherwhitelabel2.com") {
GA_MEASUREMENT_ID = 'G-45438PPXXWW';
} else if(hostname === "anotherwhitelabel3.com" || hostname === "join.anotherwhitelabel3.com") {
GA_MEASUREMENT_ID = 'G-1688PPXXWW';
} else if(hostname === "guerillaporn.com" || hostname === "join.guerillaporn.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // Production environment ID for both 'guerillaporn.com' and 'join.guerillaporn.com'
} else {
console.log("Environment not recognized or is local. GA not initialized.");
GA_MEASUREMENT_ID = undefined;
}
if (GA_MEASUREMENT_ID) {
var gtagScript = document.createElement('script');
gtagScript.async = true;
gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
document.head.appendChild(gtagScript);
gtagScript.onload = function() {
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
};
}
</script>
But – much like this new whitelabel condensing project – I still don't wnat to have to duplicate all my code for each site – when really we are measuring the same data in each site. to reuse 1 white label for many -is a GREAT THING. For analytics GTM could still be a problem – mmoving all those tags, triggers, variables , custom dimensions, etc. around for each.
BUT is it ok to do this? – well I thought maybe the robot from google would know –
SO I asked ChatGPT what it / google think:::::
(It is OK to use 1 GTM container for multiple sites – ESPECIALLY IF THEY ARE WHITE LABLES.)
MASTER + Site StreamsIDs
chatGPT answers
Using Individual SteamIDs + Additional Overall Stream ID
Sending analytics data to individual sites using separate Stream IDs, and also having an overall Stream ID that aggregates data from all sites, is not only a good practice but also a strategic approach for managing analytics at scale. This setup allows for both granular analysis at the individual site level and a holistic view across all sites. Here are several reasons why this approach can be beneficial:
Advantages:
- Segmentation and Aggregation: It allows for detailed analytics on individual sites while still enabling aggregated data analysis. This can be particularly useful for understanding site-specific behaviors and comparing them across the portfolio.
- Customized Reporting: Different sites may have unique goals, user bases, or behaviors. Separate Stream IDs allow for customized reporting and KPI tracking for each site, ensuring that insights are relevant and actionable.
- Error Isolation: If there's an issue or anomaly in data collection or reporting, having separate streams can help isolate the problem to a specific site without impacting the overall data analysis.
- Access Control: Different teams or stakeholders may be responsible for different sites. Separate Stream IDs can help manage access control, ensuring that teams only access data relevant to their sites.
- Performance Optimization: For large-scale operations, segmenting data can help in optimizing performance by reducing the load on data analysis and reporting for each individual site.
Considerations:
- Consistency in Implementation: Ensure that the tracking setup (tags, triggers, and data layers) is consistent across all sites to maintain data integrity and comparability.
- Data Overlap: When designing reports for the aggregated data, be mindful of the potential for double-counting or misinterpretation. Clear segmentation and filtering are essential.
- Management Overhead: More streams mean more configurations to manage. It's important to keep track of the configurations for each Stream ID and the overall setup to ensure data accuracy.
- Costs and Quotas: Depending on the analytics platform, there might be costs or quota limits associated with the number of streams or the volume of data collected. It's important to understand these implications.
In conclusion, utilizing separate Stream IDs for individual sites along with an overall Stream ID for aggregated data is a strategic approach for managing and analyzing web analytics. It facilitates detailed, site-specific insights while also providing a bird's-eye view of overall performance, making it easier to derive actionable intelligence and make informed decisions across your web properties.
the things it brings up to “consider” honestly could happen in just a single site – you have to be careful with tagging is all.
Then I asked about the feasabilty of usgin a single GTM container across multiple web sites.
chatGPT
Using a single Google Tag Manager (GTM) container across multiple websites, especially for white-labeled sites that share the same structure and analytics needs, can indeed simplify management and reduce the likelihood of configuration errors. It also enables centralized tracking setup, making it easier to maintain and update across all your sites. Your approach to segment data by using a different GA (Google Analytics) Measurement ID for each site, while also having a master GA Measurement ID for aggregated data, is practical and efficient.
Implementing GTM Across Multiple Sites:
- Single GTM Container: Use one GTM container across all sites. This container will hold all your tags, triggers, and variables.
- Configuration Using Hostname: As you’ve outlined, use JavaScript to dynamically set the GA Measurement ID based on the
hostname
. This allows you to customize tracking for each site while using the same GTM container. - Custom Variables in GTM: Within GTM, you can create custom variables (for example, a hostname variable) and use these to segment your data further within GA or to control tag firing conditions.
- Custom Events for Specific Actions: As shown in your examples, pushing custom events to the
dataLayer
from individual sites allows for detailed, site-specific tracking. This can be managed centrally within GTM.
Key Considerations:
- Consistency: Ensure consistent implementation across all sites. This includes the same
dataLayer
structure, naming conventions, and event handling. - Security and Privacy: Make sure that tracking setups comply with GDPR, CCPA, or any other relevant privacy regulations, especially if these sites serve users from different geographical locations.
- Access Control: GTM allows for access control and versioning. Be mindful of who has access to your GTM container and make regular use of versioning to track changes.
- Testing and Debugging: Leverage the GTM preview mode to test changes on each site before publishing. This ensures that your tracking implementation works as expected across different environments.
to me this approach give us all of it – WITHOUT opening doors to other issues – what do you think? to me – it is the simplest way to get all the data we could ever need. – presorted and managed in 1 place.
Reports
In general – click the pencil icon in the top right corner – there you can add other factors to the report – and save what you did after.
Also look for “add cards” that give you summaries of info inside other reports.
ALSO check out “Library” there are premade reports in there that you can reuse – and that is where “saved” reports go.
SOME REPORTS
Reports > User > Tech > Overview
the ususal platforms / device etc. info.
Explore – path exploration
– this takes a minute to set up – but this is where you can set up a backtrace from the purchase event to where users entered from.
https://support.google.com/analytics/answer/9317498?hl=en
https://www.analyticsmania.com/post/how-to-do-path-analysis-in-google-analytics-4/
User > User attributes > Overview – THen click penicil and add cards –
Engagement
An engaged session is a session that lasts longer than 10 seconds, has a conversion event, or has at least 2 pageviews. The engagement rate is the percentage of engaged sessions on the website. Bounce rate is calculated differently on GA4 – it’s just the opposite of engagement rate
Try changing the reports with pencil in all of these to get what you need:
– Overview
– Events
– Conversions
Conversion is any user action that’s valuable to your business; for example, a user purchasing from your store or subscribing to your newsletter are examples of common conversions.
I only set up sign_up and purchase so far.
OK – I hear Gary – but as he expressed – even with the segmenting (which does NOT always do what you think it will) there is still some manipulation of the data that has to occur AFTER everything gets up to Analytics. To make the reports – you also have to be aware of what segments you need to use and apply them correctly. Past experience has shown me – no matter how easy (premade reports I made) – or my attempts to break down what event paramaters to use to get the metrics they want in a chart – it just doesn't translate. Our users want what they want – the way they want it. Adding ALL sites together – I feel willl lesson the trust in the data we will get because they won't see what they expect. While my origijnal idea of tracking all the price points was indeed asinine – BUT Andy didn't ask me to stop becuase it wouldn't work.. He asked me to stop because it would have and they were not ready for another way for the "world" to see our $.
Additionally – we are a SPA (single page application) – which means to track all "page" views – we work with changes in the history. GTM makes that very simple to do – and allows a great ability to change what you are reading. It's still possible to get page views without GTM in a SPA – but requires a component to be built that mimics what analytics gets from normal sites. Also not a big deal – UNTIL – you want to change that fundamament metirc. Then that's a deploy etc. It makes sense to keep using GTM and to use it for all its' worth in that case – and really I don't agree with his theory that using ONLY the gtag.js is superior to GTM. GTM is a little asshole – but deservidly so – with it you can do A LOT more immediately without having to change anything in the applciation it self.
I wasn't getting login events for a nubmer of reasons (they work now) – one of the solutions I tried was to really break out the sites from eachother – we had an old UA (universal analytics call) but the gtag.js for GA4 was brannd new. GTM was already there – so adding gtag.js is new – I also wanted to avoid another tango with DevOps over these silly values – so was looking for a way to swap around the streamids without having to add it into their config.
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Let's get beta - qa - out of prod metrics for good -
var hostname = window.location.hostname.toLowerCase().trim();
var GA_MEASUREMENT_ID;
// use hostname to break out streamids - clean simple easy -
if(hostname === "dev.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Development environment ID
} else if(hostname === "stage.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Staging environment ID
} else if(hostname === "www-guerillaporn.nakedsword.org") {
GA_MEASUREMENT_ID = 'G-85Z5DGLTLN'; // QA environment ID
} else if(hostname === "shawns-machine.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // need this for testing - remove for prod.
} else if(hostname === "guerillaporn.com" || hostname === "join.guerillaporn.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // have to keep it going on nats - rocketgate has static tag but that is ok -
} else {
console.log("Check environment settings - or - not streamID present");
GA_MEASUREMENT_ID = undefined; //stop if wrong / blank
}
// set sreamID
GA_MEASUREMENT_ID
if (GA_MEASUREMENT_ID) {
var gtagScript = document.createElement('script');
gtagScript.async = true;
gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
document.head.appendChild(gtagScript);
gtagScript.onload = function() {
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
};
}
</script>
Now the GTM set up is fairly extensive – google does not have a way to "copy" coifigs around (so oculd be error prone – reentering data inot eac property. BUT you can use a lookup table and base it off hostname (like above) and use the same GTM contianer for all enviroments. It automatcally segments the data BEFORE analytics. so it won't end up everywhere.
here's an overview of what goes into tracking – and why I would like to only manage one instance of this:
To get to Gary's idea.
So if getting the environments separated out works – why not just expand it – AND – add a OVERALL stream ID – that collects all data from all sites – it will give us everything we want – without polluting the individual sites.
<!-- MASTER OVERALL Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MASTER123"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-MASTER123);
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Environment-specific configuration
var hostname = window.location.hostname.toLowerCase().trim();
var GA_MEASUREMENT_ID;
if(hostname === "dev.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Development environment ID
} else if(hostname === "stage.guerilla.com") {
GA_MEASUREMENT_ID = 'G-DMTDH2L5EJ'; // Staging environment ID
} else if(hostname === "www-guerillaporn.nakedsword.org") {
GA_MEASUREMENT_ID = 'G-85Z5DGLTLN'; // QA environment ID
} else if(hostname === "shawns-machine.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW';
} else if(hostname === "anotherwhitelabel1.com") {
GA_MEASUREMENT_ID = 'G-5538PPXXWW';
} else if(hostname === "anotherwhitelabel2.com") {
GA_MEASUREMENT_ID = 'G-45438PPXXWW';
} else if(hostname === "anotherwhitelabel3.com" || hostname === "join.anotherwhitelabel3.com") {
GA_MEASUREMENT_ID = 'G-1688PPXXWW';
} else if(hostname === "guerillaporn.com" || hostname === "join.guerillaporn.com") {
GA_MEASUREMENT_ID = 'G-1688HPPTWW'; // Production environment ID for both 'guerillaporn.com' and 'join.guerillaporn.com'
} else {
console.log("Environment not recognized or is local. GA not initialized.");
GA_MEASUREMENT_ID = undefined;
}
if (GA_MEASUREMENT_ID) {
var gtagScript = document.createElement('script');
gtagScript.async = true;
gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
document.head.appendChild(gtagScript);
gtagScript.onload = function() {
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
};
}
</script>
But – much like this new whitelabel condensing project – I still don't wnat to have to duplicate all my code for each site – when really we are measuring the same data in each site. to reuse 1 white label for many -is a GREAT THING. For analytics GTM could still be a problem – mmoving all those tags, triggers, variables , custom dimensions, etc. around for each.
BUT is it ok to do this? – well I thought maybe the robot from google would know –
SO I asked ChatGPT what it / google think:::::
(It is OK to use 1 GTM container for multiple sites – ESPECIALLY IF THEY ARE WHITE LABLES.)
MASTER + Site StreamsIDs
chatGPT answers
Using Individual SteamIDs + Additional Overall Stream ID
Sending analytics data to individual sites using separate Stream IDs, and also having an overall Stream ID that aggregates data from all sites, is not only a good practice but also a strategic approach for managing analytics at scale. This setup allows for both granular analysis at the individual site level and a holistic view across all sites. Here are several reasons why this approach can be beneficial:
Advantages:
- Segmentation and Aggregation: It allows for detailed analytics on individual sites while still enabling aggregated data analysis. This can be particularly useful for understanding site-specific behaviors and comparing them across the portfolio.
- Customized Reporting: Different sites may have unique goals, user bases, or behaviors. Separate Stream IDs allow for customized reporting and KPI tracking for each site, ensuring that insights are relevant and actionable.
- Error Isolation: If there's an issue or anomaly in data collection or reporting, having separate streams can help isolate the problem to a specific site without impacting the overall data analysis.
- Access Control: Different teams or stakeholders may be responsible for different sites. Separate Stream IDs can help manage access control, ensuring that teams only access data relevant to their sites.
- Performance Optimization: For large-scale operations, segmenting data can help in optimizing performance by reducing the load on data analysis and reporting for each individual site.
Considerations:
- Consistency in Implementation: Ensure that the tracking setup (tags, triggers, and data layers) is consistent across all sites to maintain data integrity and comparability.
- Data Overlap: When designing reports for the aggregated data, be mindful of the potential for double-counting or misinterpretation. Clear segmentation and filtering are essential.
- Management Overhead: More streams mean more configurations to manage. It's important to keep track of the configurations for each Stream ID and the overall setup to ensure data accuracy.
- Costs and Quotas: Depending on the analytics platform, there might be costs or quota limits associated with the number of streams or the volume of data collected. It's important to understand these implications.
In conclusion, utilizing separate Stream IDs for individual sites along with an overall Stream ID for aggregated data is a strategic approach for managing and analyzing web analytics. It facilitates detailed, site-specific insights while also providing a bird's-eye view of overall performance, making it easier to derive actionable intelligence and make informed decisions across your web properties.
the things it brings up to “consider” honestly could happen in just a single site – you have to be careful with tagging is all.
Then I asked about the feasabilty of usgin a single GTM container across multiple web sites.
chatGPT
Using a single Google Tag Manager (GTM) container across multiple websites, especially for white-labeled sites that share the same structure and analytics needs, can indeed simplify management and reduce the likelihood of configuration errors. It also enables centralized tracking setup, making it easier to maintain and update across all your sites. Your approach to segment data by using a different GA (Google Analytics) Measurement ID for each site, while also having a master GA Measurement ID for aggregated data, is practical and efficient.
Implementing GTM Across Multiple Sites:
- Single GTM Container: Use one GTM container across all sites. This container will hold all your tags, triggers, and variables.
- Configuration Using Hostname: As you’ve outlined, use JavaScript to dynamically set the GA Measurement ID based on the
hostname
. This allows you to customize tracking for each site while using the same GTM container. - Custom Variables in GTM: Within GTM, you can create custom variables (for example, a hostname variable) and use these to segment your data further within GA or to control tag firing conditions.
- Custom Events for Specific Actions: As shown in your examples, pushing custom events to the
dataLayer
from individual sites allows for detailed, site-specific tracking. This can be managed centrally within GTM.
Key Considerations:
- Consistency: Ensure consistent implementation across all sites. This includes the same
dataLayer
structure, naming conventions, and event handling. - Security and Privacy: Make sure that tracking setups comply with GDPR, CCPA, or any other relevant privacy regulations, especially if these sites serve users from different geographical locations.
- Access Control: GTM allows for access control and versioning. Be mindful of who has access to your GTM container and make regular use of versioning to track changes.
- Testing and Debugging: Leverage the GTM preview mode to test changes on each site before publishing. This ensures that your tracking implementation works as expected across different environments.
to me this approach give us all of it – WITHOUT opening doors to other issues – what do you think? to me – it is the simplest way to get all the data we could ever need. – presorted and managed in 1 place.
Reports
In general – click the pencil icon in the top right corner – there you can add other factors to the report – and save what you did after.
Also look for “add cards” that give you summaries of info inside other reports.
ALSO check out “Library” there are premade reports in there that you can reuse – and that is where “saved” reports go.
SOME REPORTS
Reports > User > Tech > Overview
the ususal platforms / device etc. info.
Explore – path exploration
– this takes a minute to set up – but this is where you can set up a backtrace from the purchase event to where users entered from.
https://support.google.com/analytics/answer/9317498?hl=en
https://www.analyticsmania.com/post/how-to-do-path-analysis-in-google-analytics-4/
User > User attributes > Overview – THen click penicil and add cards –
Engagement
An engaged session is a session that lasts longer than 10 seconds, has a conversion event, or has at least 2 pageviews. The engagement rate is the percentage of engaged sessions on the website. Bounce rate is calculated differently on GA4 – it’s just the opposite of engagement rate
Try changing the reports with pencil in all of these to get what you need:
– Overview
– Events
– Conversions
Conversion is any user action that’s valuable to your business; for example, a user purchasing from your store or subscribing to your newsletter are examples of common conversions.
I only set up sign_up and purchase so far.
https://support.google.com/analytics/answer/6366371?hl=en#zippy=%2Cin-this-article