Skip to content
Notifications
Clear all

Walkthrough: Recreating a classic Google Analytics report after GA4.

1 Posts
1 Users
0 Reactions
29 Views
(@sre_night_shift_3)
Eminent Member
Joined: 5 months ago
Posts: 19
Topic starter   [#188]

Alright, so like many of you, I've been navigating the GA4 transition. The new data model is powerful, but sometimes I just need to quickly recreate a simple, classic report I used to rely on. My team was missing a clear view of top landing pages with their bounce rate—a staple for spotting content or UX issues.

I figured this was a good excuse to push our observability stack a bit. Instead of living in the GA4 UI, I used the BigQuery export and Grafana to build something durable and alertable. Here's the core query I landed on to mimic that old report. It's for the last 7 days, focusing on session start events.

```sql
SELECT
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS landing_page,
COUNT(DISTINCT CONCAT(user_pseudo_id, CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS STRING))) AS sessions,
COUNT(DISTINCT CASE WHEN (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'session_engaged') = '1' THEN CONCAT(user_pseudo_id, CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS STRING)) END) AS engaged_sessions,
ROUND(1 - (COUNT(DISTINCT CASE WHEN (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'session_engaged') = '1' THEN CONCAT(user_pseudo_id, CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS STRING)) END) / COUNT(DISTINCT CONCAT(user_pseudo_id, CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS STRING)))), 2) AS bounce_rate
FROM
`your_project.your_dataset.events_*`
WHERE
_TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)) AND FORMAT_DATE('%Y%m%d', CURRENT_DATE())
AND event_name = 'session_start'
GROUP BY
1
ORDER BY
sessions DESC
LIMIT 20;
```

Key things I noted:
* The `session_engaged` parameter is the new gatekeeper for a "non-bounce." It's not a perfect 1:1 with the old definition, but it's the GA4 standard.
* Session stitching (`user_pseudo_id` + `ga_session_id`) is crucial for accurate counts. I've seen drift without it.
* I pipe this into Grafana with a simple table panel. Even added a threshold for bounce_rate > 75% to highlight rows in red. Now it's on a team dashboard, and we can set an alert if a critical page suddenly spikes in bounce rate—ties directly into our on-call response for site reliability.

The process really drove home the need to treat our analytics data like any other telemetry stream: queryable, alertable, and integrated. It's less about recreating the old report exactly and more about building the *insight* back into our operational view.

Has anyone else built GA4-derived metrics into their monitoring or alerting pipelines? Curious about your approaches.

-- nightowl


nightowl


   
Quote