Add Google Analytics #617
Add Google Analytics #617
Conversation
Codecov Report
@@ Coverage Diff @@
## master #617 +/- ##
==========================================
- Coverage 62.81% 62.69% -0.12%
==========================================
Files 130 131 +1
Lines 7296 7345 +49
Branches 1616 1623 +7
==========================================
+ Hits 4583 4605 +22
- Misses 2390 2411 +21
- Partials 323 329 +6
Continue to review full report at Codecov.
|
|
The legal bug for permission to analytics is approved. If anyone has thoughts on my implementation please leave them here. Otherwise, @julienw I would appreciate a code review on this so we can started getting some data about our usage patterns |
|
There are a few needed changes but I think overall this looks good :) Thanks ! |
| timingLabel?: string, | ||
| |}; | ||
|
|
||
| type GoogleAnalytics = ('send', GAEvent | GAPageView | GATiming) => {}; |
julienw
Oct 27, 2017
Contributor
Any reason you declare all this here and not in a separate types file ? Like src/types/analytics.js ?
Then you can import it here yo use it below.
Any reason you declare all this here and not in a separate types file ? Like src/types/analytics.js ?
Then you can import it here yo use it below.
| @@ -25,7 +25,8 @@ new WebpackDevServer(webpack(config), { | |||
| 'self' | |||
| 'sha256-eRTCQnd2fhPykpATDzCv4gdVk/EOdDq+6yzFXaWgGEw=' | |||
| 'sha256-AdiT28wTL5FNaRVHWQVFC0ic3E20Gu4/PiC9xukS9+E=' | |||
| https://api-ssl.bitly.com; | |||
| https://api-ssl.bitly.com | |||
| https://www.google-analytics.com; | |||
julienw
Oct 27, 2017
Contributor
Not sure this is needed given we load it in perf.html only ? Though it can be useful while testing.
Not sure this is needed given we load it in perf.html only ? Though it can be useful while testing.
gregtatum
Nov 1, 2017
Author
Member
Maybe not technically, but it does make it easier to verify they are the same.
Maybe not technically, but it does make it easier to verify they are the same.
| // Only include on perf-html.io | ||
| window.location.host.includes('perf-html.io') && | ||
| // Observe do not track. | ||
| !(navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack) |
julienw
Oct 27, 2017
Contributor
doNotTrack values are not boolean. You want to check if the value is "1", maybe something like:
const doNotTrack = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack) === '1';
if (!doNotTrack) { ... }
doNotTrack values are not boolean. You want to check if the value is "1", maybe something like:
const doNotTrack = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack) === '1';
if (!doNotTrack) { ... }
gregtatum
Nov 1, 2017
Author
Member
Yikes, what an API. Fixing it.
Yikes, what an API. Fixing it.
| (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | ||
| m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | ||
| })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | ||
| ga('create', 'UA-35433268-81', 'auto'); |
julienw
Oct 27, 2017
Contributor
wondering if this shouldn't be hidden from our source, and instead we should have a conf file for building on the server ?
wondering if this shouldn't be hidden from our source, and instead we should have a conf file for building on the server ?
julienw
Oct 27, 2017
Contributor
I assume this is our ID.
I assume this is our ID.
gregtatum
Nov 1, 2017
Author
Member
This is our ID, although it's always publicly visible. I'm not sure it's worth the complexity of adding environment keys to our build step yet.
This is our ID, although it's always publicly visible. I'm not sure it's worth the complexity of adding environment keys to our build step yet.
| @@ -6,6 +6,7 @@ | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <title>perf.html</title> | |||
| <script src='analytics.js'></script> | |||
julienw
Oct 27, 2017
Contributor
Should you add it in webpack's OfflinePlugin configuration too ?
Should you add it in webpack's OfflinePlugin configuration too ?
gregtatum
Nov 1, 2017
Author
Member
Done.
Done.
julienw
Nov 14, 2017
Contributor
Actually, you need /analytics.js instead of analytics.js
Actually, you need /analytics.js instead of analytics.js
julienw
Nov 14, 2017
Contributor
And you need to copy it to dist.
And you need to copy it to dist.
| ga('send', { | ||
| hitType: 'event', | ||
| eventCategory: 'profile', | ||
| eventAction: 'change hide platform details', |
julienw
Oct 27, 2017
Contributor
not sure this is the right action string ;) "change implementation filter" would be more appropriate, right ?
not sure this is the right action string ;) "change implementation filter" would be more appropriate, right ?
gregtatum
Nov 1, 2017
Author
Member
Nice catch, thanks!
Nice catch, thanks!
| eventCategory: 'profile upload', | ||
| eventAction: 'failed', | ||
| }); | ||
| } |
julienw
Oct 27, 2017
Contributor
I'd create an action (I mean, an action that wouldn't dispatch a redux action) to handle ga calls.
or at least a local function.
I'd create an action (I mean, an action that wouldn't dispatch a redux action) to handle ga calls.
or at least a local function.
gregtatum
Nov 1, 2017
Author
Member
I'm curious if the reason you want me to create local functions is the if check. Would you be ok with me stubbing out the analytics function for everywhere? That way we can fire it regardless. The interface itself is just a function call with some parameters, so I don't feel the need to abstract it away further.
I'm curious if the reason you want me to create local functions is the if check. Would you be ok with me stubbing out the analytics function for everywhere? That way we can fire it regardless. The interface itself is just a function call with some parameters, so I don't feel the need to abstract it away further.
| updateUrlState, | ||
| urlSetupDone, | ||
| show404, | ||
| } |
julienw
Oct 27, 2017
Contributor
👍
it always disturbed me
it always disturbed me
|
|
||
| // Some portion of users will have timing information sent. Limit this further to | ||
| // only send a few labels per user. | ||
| const ga = self.ga; |
julienw
Oct 27, 2017
Contributor
yet another way of getting the ga function ? :)
yet another way of getting the ga function ? :)
gregtatum
Oct 27, 2017
Author
Member
window doesn't exist in workers.
window doesn't exist in workers.
julienw
Oct 28, 2017
Contributor
Ok, makes sense !
Ok, makes sense !
| // Some portion of users will have timing information sent. Limit this further to | ||
| // only send a few labels per user. | ||
| const ga = self.ga; | ||
| if (ga && !(_timingsPerLabel[label] > MAX_TIMINGS_PER_LABEL)) { |
julienw
Oct 27, 2017
Contributor
I think you want _timingsPerLabel[label] < MAX_TIMINGS_PER_LABEL (I mean, remove the ! of course, and use < instead of the current <= because otherwise it happens once too many)
I think you want _timingsPerLabel[label] < MAX_TIMINGS_PER_LABEL (I mean, remove the ! of course, and use < instead of the current <= because otherwise it happens once too many)
gregtatum
Nov 1, 2017
Author
Member
Thanks, not sure why I had it that way.
Thanks, not sure why I had it that way.
|
Some general comments and one point on how we track page views |
| (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | ||
| m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | ||
| })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | ||
| ga('create', 'UA-35433268-81', 'auto'); |
digitarald
Oct 30, 2017
Contributor
to be more specific, auto could be also perf-html.io to tracking on other domains; this way the check can be removed from the top-level if
to be more specific, auto could be also perf-html.io to tracking on other domains; this way the check can be removed from the top-level if
gregtatum
Nov 2, 2017
Author
Member
I personally like the top level if, since it won't even ping google while I'm working locally, and have spotty internet.
I personally like the top level if, since it won't even ping google while I'm working locally, and have spotty internet.
| m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | ||
| })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | ||
| ga('create', 'UA-35433268-81', 'auto'); | ||
| ga('send', 'pageview'); |
digitarald
Oct 30, 2017
Contributor
This should probably not send the current URL but the selectedTab from the profile viewer; otherwise the metrics will be a mix of paths and selected tabs
This should probably not send the current URL but the selectedTab from the profile viewer; otherwise the metrics will be a mix of paths and selected tabs
digitarald
Oct 30, 2017
Contributor
Seems like that is done in https://github.com/devtools-html/perf.html/pull/617/files#diff-0bc320ad3ce513416aecd6152b478911R56 so this line is probably obsolete
Seems like that is done in https://github.com/devtools-html/perf.html/pull/617/files#diff-0bc320ad3ce513416aecd6152b478911R56 so this line is probably obsolete
gregtatum
Nov 1, 2017
Author
Member
Yes, that was a mistake leaving that in there, thanks for catching it!
Yes, that was a mistake leaving that in there, thanks for catching it!
| ga('send', { | ||
| hitType: 'event', | ||
| eventCategory: 'profile', | ||
| eventAction: 'toggle thread', |
digitarald
Oct 30, 2017
Contributor
Could we use hide for the action and the type (content, main, compositor, etc) for the label? This way we know which threads most users hide (if users actually hide threads, might need to be more visible) and might pro-actively show less of them
Could we use hide for the action and the type (content, main, compositor, etc) for the label? This way we know which threads most users hide (if users actually hide threads, might need to be more visible) and might pro-actively show less of them
| const dataSource = getDataSource(getState()); | ||
| ga('send', { | ||
| hitType: 'pageview', | ||
| page: dataSource === 'none' ? 'home' : getSelectedTab(getState()), |
digitarald
Oct 30, 2017
Contributor
What would be interesting here where the user came from. First idea that comes to mind would be adding an event to when the profile is loaded either from whatever mechanism is triggered.
What would be interesting here where the user came from. First idea that comes to mind would be adding an event to when the profile is loaded either from whatever mechanism is triggered.
gregtatum
Nov 1, 2017
Author
Member
ga('send', {
hitType: 'event',
eventCategory: 'datasource',
eventAction: dataSource,
});
ga('send', {
hitType: 'event',
eventCategory: 'datasource',
eventAction: dataSource,
});
a7534a5
to
0b5afa6
|
Hey, thanks for the changes, this is much more readable ! There are 2 subtle bugs left:
|
| // Only include on perf-html.io | ||
| window.location.host.includes('perf-html.io') && | ||
| // Observe do not track. | ||
| (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack) === '1' |
julienw
Nov 6, 2017
Contributor
don't you want the opposite ? ;)
It's '1' if the user actually specified "I don't want to be tracked" ;)
That's why my suggested code was:
const doNotTrack = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack) === '1';
if (!doNotTrack) { ... }
(note the ! :) )
don't you want the opposite ? ;)
It's '1' if the user actually specified "I don't want to be tracked" ;)
That's why my suggested code was:
const doNotTrack = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack) === '1';
if (!doNotTrack) { ... }(note the ! :) )
|
|
||
| // Some portion of users will have timing information sent. Limit this further to | ||
| // only send a few labels per user. | ||
| if (_timingsPerLabel[label] <= MAX_TIMINGS_PER_LABEL) { |
julienw
Nov 6, 2017
Contributor
When _timingsPerLabel[label] is undefined (the first time then), I think the result of this comparison is always false.
I think you need to set it to a temporary const before the condition, something like this:
const sentTimings = _timingsPerLabel[label] || 0;
if (sentTimings < MAX_TIMINGS_PER_LABEL) {
_timingsPerLabel[label] = sentTimings + 1;
...
}
Also note the operator < instead of <=. Otherwise I think it's sent (MAX_TIMINGS_PER_LABEL + 1) times.
When _timingsPerLabel[label] is undefined (the first time then), I think the result of this comparison is always false.
I think you need to set it to a temporary const before the condition, something like this:
const sentTimings = _timingsPerLabel[label] || 0;
if (sentTimings < MAX_TIMINGS_PER_LABEL) {
_timingsPerLabel[label] = sentTimings + 1;
...
}Also note the operator < instead of <=. Otherwise I think it's sent (MAX_TIMINGS_PER_LABEL + 1) times.
gregtatum
Nov 10, 2017
Author
Member
Thanks for catching this.
Thanks for catching this.
| export type GoogleAnalytics = ('send', GAPayload) => {}; | ||
|
|
||
| export function sendAnalytics(payload: GAPayload) { | ||
| const { ga } = self; |
julienw
Nov 6, 2017
Contributor
I think there is something wrong after latest Flow upgrade, the typing for window properties seems to be broken.
In the mean time, could you type ga manually here ?
I think there is something wrong after latest Flow upgrade, the typing for window properties seems to be broken.
In the mean time, could you type ga manually here ?
gregtatum
Nov 10, 2017
Author
Member
This must be because self is not equivalent to window.
This must be because self is not equivalent to window.
|
@julienw this is ready for re-review. |
| @@ -6,6 +6,7 @@ | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <title>perf.html</title> | |||
| <script src='analytics.js'></script> | |||
julienw
Nov 14, 2017
Contributor
Actually, you need /analytics.js instead of analytics.js
Actually, you need /analytics.js instead of analytics.js
| @@ -6,6 +6,7 @@ | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <title>perf.html</title> | |||
| <script src='analytics.js'></script> | |||
julienw
Nov 14, 2017
Contributor
And you need to copy it to dist.
And you need to copy it to dist.
| @@ -9,7 +9,7 @@ | |||
| (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |||
| (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |||
| m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |||
| })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |||
| })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); | |||
julienw
Nov 14, 2017
Contributor
without this change, CSP blocks the "http" version in the dev environment. 2 possible fixes: either this, or adding the "http" version to the CSP header -- which redirects to https anyway. So I think it's better to always ask for the https version, in this case.
without this change, CSP blocks the "http" version in the dev environment. 2 possible fixes: either this, or adding the "http" version to the CSP header -- which redirects to https anyway. So I think it's better to always ask for the https version, in this case.
| @@ -6,7 +6,7 @@ | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <title>perf.html</title> | |||
| <script src='analytics.js'></script> | |||
| <script src='/analytics.js'></script> | |||
julienw
Nov 14, 2017
Contributor
without this change, when on an URL like http://localhost:4242/public/d3fd7e63272e130fdc3ae54b861e8586f5a0d385/calltree/, this looks for http://localhost:4242/public/d3fd7e63272e130fdc3ae54b861e8586f5a0d385/calltree/analytics.js ;)
without this change, when on an URL like http://localhost:4242/public/d3fd7e63272e130fdc3ae54b861e8586f5a0d385/calltree/, this looks for http://localhost:4242/public/d3fd7e63272e130fdc3ae54b861e8586f5a0d385/calltree/analytics.js ;)
|
Note I think we should do the actions from #473 before landing @gregtatum @digitarald. Or at least before deploying. |
|
@gregtatum if you're OK with my last commit, please merge :) |
|
The review is still in the state "changes requested" but I'm using my admin privilege to go ahead and merge based on your written approval. |
#615 needs to land first.
I opted to go for pageviews on the tabs, as then we can easily gather timing information through the GA reports. I also added instrumentation to timeCode, so that we can get that information and analyze it over a broader audience.
I tried to break out the changes into different commits in case people wanted to look at them individually for feedback.