-
Notifications
You must be signed in to change notification settings - Fork 102
Add event tracking use GA #6940
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
cb030e7
setup link
yangw-dev 2fcb4ec
setup link
yangw-dev 24389b8
setup link
yangw-dev e77a954
setup link
yangw-dev 70c91ac
setup link
yangw-dev dc900b1
Update track.ts
yangw-dev 9884694
Update event_tracking_guidance.md
yangw-dev 53595c1
Update torchci/docs/event_tracking_guidance.md
yangw-dev 163b029
setup link
yangw-dev f9a1477
setup link
yangw-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# Event Tracking guidance | ||
yangw-dev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Overview | ||
|
||
Guidance for event tracking in torchci. | ||
|
||
## Google Analytics Development Guide (Local Development) | ||
|
||
### Overview | ||
|
||
This guide explains how to enable Google Analytics 4 (GA4) debug mode during local development so you can verify event tracking in real time via GA DebugView. | ||
|
||
### Prerequisites | ||
|
||
- TorchCI front-end development environment is set up and running locally | ||
- Chrome browser installed | ||
- Install chrome extension [Google Analytics Debugger](https://chrome.google.com/webstore/detail/jnkmfdileelhofjcijamephohjechhna) | ||
- Make sure you have permission to the GCP project `pytorch-hud` as admin. If not, reach out to `oss support (internal only)` or @pytorch/pytorch-dev-infra to add you | ||
|
||
## Steps | ||
|
||
### 1. Append `?debug_mode=true` to Your Local URL | ||
|
||
Go to the page you want to testing the tracking event, and add parameter `?debug_mode=true` | ||
Example: | ||
|
||
``` | ||
http://localhost:3000/queue_time_analysis?debug_mode=true | ||
``` | ||
|
||
you should see the QA debugging info in console: | ||
|
||
### View debug view in Google Analytics | ||
|
||
[Analytics DebugView](https://analytics.google.com/analytics/web/#/a44373548p420079840/admin/debugview/overview) | ||
|
||
When click a tracking button or event, you should be able to see it logged in the debugview (it may have 2-15 secs delayed). | ||
|
||
### Adding event to track | ||
|
||
two options to add event: | ||
|
||
#### data attribute | ||
|
||
Provided customized listener to catch tracking event using data-attributes | ||
|
||
This is used to track simple user behaviours. | ||
|
||
```tsx | ||
Example usage: | ||
<button | ||
data-ga-action="signup_click" | ||
data-ga-label="nav_button" | ||
data-ga-category="cta" | ||
data-ga-event-types="click" | ||
> | ||
Sign Up | ||
</button> | ||
``` | ||
|
||
Supported data attributes: | ||
|
||
- `data-ga-action` (required): GA action name | ||
- `data-ga-category` (optional): GA category (defaults to event type) | ||
- `data-ga-label` (optional): GA label | ||
- `data-ga-event-types` (optional): comma-separated list of allowed event types for this element (e.g. "click,submit") | ||
|
||
#### using trackEventWithContext | ||
|
||
using trackEventWithContext to provide extra content. | ||
|
||
```tsx | ||
trackEventWithContext( | ||
action: string, | ||
category?: string, | ||
label?: string, | ||
extra?: Record<string, any> | ||
) | ||
``` |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { trackEventWithContext } from "./track"; | ||
|
||
/** | ||
* Sets up global GA event tracking for DOM elements using `data-ga-*` attributes. | ||
* | ||
* 🔍 This enables declarative analytics tracking by simply adding attributes to HTML elements. | ||
* You can limit tracking to specific DOM event types (e.g., "click") both globally and per-element. | ||
* | ||
* Example usage (in _app.tsx or layout): | ||
* useEffect(() => { | ||
* const teardown = setupGAAttributeEventTracking(["click", "submit"]); | ||
* return teardown; // cleanup on unmount | ||
* }, []); | ||
* | ||
* Example usage: | ||
* <button | ||
* data-ga-action="signup_click" | ||
* data-ga-label="nav_button" | ||
* data-ga-category="cta" | ||
* data-ga-event-types="click" | ||
* > | ||
* Sign Up | ||
* </button> | ||
* | ||
* Supported data attributes: | ||
* - `data-ga-action` (required): GA action name | ||
* - `data-ga-label` (optional): GA label | ||
* - `data-ga-category` (optional): GA category (defaults to event type) | ||
* - `data-ga-event-types` (optional): comma-separated list of allowed event types for this element (e.g. "click,submit") | ||
* | ||
* @param globalEventTypes - Array of DOM event types to listen for globally (default: ["click", "change", "submit", "mouseenter"]) | ||
* @returns Cleanup function to remove all added event listeners | ||
*/ | ||
export function setupGAAttributeEventTracking( | ||
globalEventTypes: string[] = ["click", "change", "submit", "mouseenter"] | ||
): () => void { | ||
const handler = (e: Event) => { | ||
const target = e.target as HTMLElement | null; | ||
if (!target) return; | ||
|
||
const el = target.closest("[data-ga-action]") as HTMLElement | null; | ||
if (!el) return; | ||
|
||
const action = el.dataset.gaAction; | ||
if (!action) return; | ||
|
||
// Check if this element has a restricted set of allowed event types | ||
const allowedTypes = el.dataset.gaEventTypes | ||
?.split(",") | ||
.map((t) => t.trim()); | ||
if (allowedTypes && !allowedTypes.includes(e.type)) { | ||
return; // This event type is not allowed for this element | ||
} | ||
|
||
const label = el.dataset.gaLabel; | ||
const category = el.dataset.gaCategory || e.type; // Default category to event type if not provided | ||
|
||
// Construct event parameters for GA4 | ||
const eventParams = { | ||
category, | ||
label, | ||
url: window.location.href, | ||
windowPathname: window.location.pathname, | ||
}; | ||
|
||
trackEventWithContext(action, category, label); | ||
}; | ||
|
||
// Add event listeners | ||
globalEventTypes.forEach((eventType) => { | ||
document.addEventListener(eventType, handler, true); // Use `true` for capture phase to catch events early | ||
}); | ||
|
||
// Return cleanup function | ||
return () => { | ||
globalEventTypes.forEach((eventType) => { | ||
document.removeEventListener(eventType, handler, true); | ||
}); | ||
}; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import { NextRouter } from "next/router"; | ||
import ReactGA from "react-ga4"; | ||
|
||
const GA_SESSION_ID = "ga_session_id"; | ||
const GA_MEASUREMENT_ID = "G-HZEXJ323ZF"; | ||
|
||
// Add a global flag to window object | ||
declare global { | ||
interface Window { | ||
__GA_INITIALIZED__?: boolean; | ||
gtag?: (...args: any[]) => void; // Declare gtag for direct access check | ||
} | ||
} | ||
|
||
export const isGaInitialized = (): boolean => { | ||
return typeof window !== "undefined" && !!window.__GA_INITIALIZED__; | ||
}; | ||
|
||
function isDebugMode() { | ||
return ( | ||
typeof window !== "undefined" && | ||
window.location.search.includes("debug_mode=true") | ||
); | ||
} | ||
|
||
function isProdEnv() { | ||
return ( | ||
typeof window !== "undefined" && | ||
window.location.href.startsWith("https://hud.pytorch.org") | ||
); | ||
} | ||
|
||
function isGAEnabled(): boolean { | ||
if (typeof window === "undefined") return false; | ||
return isDebugMode() || isProdEnv(); | ||
} | ||
|
||
/** | ||
* initialize google analytics | ||
* if withUserId is set, we generate random sessionId to track action sequence for a single page flow. | ||
* Notice, we use session storage, if user create a new page tab due to navigation, it's considered new session | ||
* @param withUserId | ||
* @returns | ||
*/ | ||
export const initGaAnalytics = (withSessionId = false) => { | ||
// Block in non-production deployments unless the debug_mode is set to true in url. | ||
if (!isGAEnabled()) { | ||
console.info("[GA] Skipping GA init"); | ||
return; | ||
} | ||
|
||
if (isGaInitialized()) { | ||
console.log("ReactGA already initialized."); | ||
return; | ||
} | ||
|
||
ReactGA.initialize(GA_MEASUREMENT_ID, { | ||
// For enabling debug mode for GA4, the primary option is `debug: true` | ||
// passed directly to ReactGA.initialize. | ||
// The `gaOptions` and `gtagOptions` are for more advanced configurations | ||
// directly passed to the underlying GA/Gtag library. | ||
// @ts-ignore | ||
debug: isDebugMode(), | ||
gaOptions: { | ||
debug_mode: isDebugMode(), | ||
}, | ||
gtagOptions: { | ||
debug_mode: isDebugMode(), | ||
cookie_domain: isDebugMode() ? "none" : "auto", | ||
}, | ||
}); | ||
|
||
window.__GA_INITIALIZED__ = true; // Set a global flag | ||
|
||
// generate random userId in session storage. | ||
if (withSessionId) { | ||
let id = sessionStorage.getItem(GA_SESSION_ID); | ||
if (!id) { | ||
id = crypto.randomUUID(); | ||
sessionStorage.setItem(GA_SESSION_ID, id); | ||
} | ||
ReactGA.set({ user_id: id }); | ||
} | ||
}; | ||
|
||
export function trackRouteEvent( | ||
router: NextRouter, | ||
eventName: string, | ||
info: Record<string, any> = {} | ||
) { | ||
if (!isGAEnabled()) { | ||
return; | ||
} | ||
|
||
const payload = { | ||
...info, | ||
url: window.location.href, | ||
windowPathname: window.location.pathname, | ||
routerPathname: router.pathname, | ||
routerPath: router.asPath, | ||
...(isDebugMode() ? { debug_mode: true } : {}), | ||
}; | ||
|
||
ReactGA.event(eventName.toLowerCase(), payload); | ||
} | ||
|
||
/** | ||
* track event with context using QA | ||
* @param action | ||
* @param category | ||
* @param label | ||
* @param extra | ||
* @returns | ||
*/ | ||
export function trackEventWithContext( | ||
action: string, | ||
category?: string, | ||
label?: string, | ||
extra?: Record<string, any> | ||
) { | ||
if (!isGAEnabled()) { | ||
return; | ||
} | ||
const payload = { | ||
category, | ||
label, | ||
event_time: new Date().toISOString(), | ||
page_title: document.title, | ||
session_id: sessionStorage.getItem(GA_SESSION_ID) ?? undefined, | ||
|
||
...(isDebugMode() ? { debug_mode: true } : {}), | ||
}; | ||
ReactGA.event(action, payload); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how to us the data-ga atrributes