feat(metrics): add flow.attempt_signin, flow.engage, flow.attempt_signup #4150
Conversation
|
added questions |
| @@ -90,7 +90,7 @@ define(function (require, exports, module) { | |||
|
|
|||
| _logPromptExperimentEvent: function (eventNameSuffix) { | |||
| const eventName = 'experiment.pw_prompt.' + eventNameSuffix.toLowerCase(); | |||
| this.logViewEvent(eventName); | |||
| this.logEventOnce(eventName); | |||
vladikoff
Sep 14, 2016
Author
Contributor
to avoid spamming pw_strength focus
to avoid spamming pw_strength focus
| engageForm() { | ||
| // user has engaged with the signin or sign up form | ||
| // log 'flow.engage' from both forms because both forms can login. | ||
| this.logEventOnce('flow.engage'); |
vladikoff
Sep 14, 2016
Author
Contributor
only log it once per page reload, which should match new flow.begin events right?
only log it once per page reload, which should match new flow.begin events right?
| flow_id: metrics.flowId, //eslint-disable-line camelcase | ||
| flow_time: 0, //eslint-disable-line camelcase | ||
| time: metrics.flowBeginTime | ||
| }, req); | ||
| } | ||
| }); |
vladikoff
Sep 14, 2016
Author
Contributor
do we want to differentiate flow.begin from other events?
do we want to differentiate flow.begin from other events?
philbooth
Sep 15, 2016
Contributor
Not sure I understand the question?
Not sure I understand the question?
vladikoff
Sep 15, 2016
Author
Contributor
I just was not sure if all flow events should be logged the same away, seems like we should just log all of them the same way (like flow.begin already was).
I just was not sure if all flow events should be logged the same away, seems like we should just log all of them the same way (like flow.begin already was).
|
This also fixes #4022 |
|
cc @rfk ^ |
|
love it, this will make for some really great analysis opportunities |
| * @param {String} eventName | ||
| */ | ||
| logEventOnce: function (eventName) { | ||
| if (eventMemory.indexOf(eventName) === -1) { |
rfk
Sep 15, 2016
Member
Could use a map or set here for constant-time lookup, this lookup will grow with O(number of events).
Could use a map or set here for constant-time lookup, this lookup will grow with O(number of events).
vladikoff
Sep 15, 2016
Author
Contributor
We don't have Map or Set available. However I updated this to use underscore which should be more performant hopefully.
We don't have Map or Set available. However I updated this to use underscore which should be more performant hopefully.
rfk
Sep 16, 2016
Member
That was bad vocabulary on my part, what I really meant was to use one of these: {}.
That was bad vocabulary on my part, what I really meant was to use one of these: {}.
| */ | ||
| logEventOnce: function (eventName) { | ||
| this.metrics.logEventOnce(eventName); | ||
| }, |
rfk
Sep 15, 2016
Member
I really like this helper, will reduce a lot of confusion when reviewing the metrics.
I really like this helper, will reduce a lot of confusion when reviewing the metrics.
|
Looking good @vladikoff! Some inline comments about event names, we need to get them consistent with the spec one way or another. |
| engageForm() { | ||
| // user has engaged with the signin or sign up form | ||
| // log 'flow.engage' from both forms because both forms can login. | ||
| this.logEventOnce('flow.engage'); |
philbooth
Sep 15, 2016
Contributor
Re: the name of this event, it is currently specced differently to this. Now the spec can change, of course, but also I want to make sure that the event we emit is the one that is most useful for analysis purposes.
For flows that end at this point, where the user never submits the form, it might be useful to know which form the user was engaged with when they abandoned the flow. How tricky is it to change this event name so that it is signup.engage or signin.engage? What about force_auth.engage?
Re: the name of this event, it is currently specced differently to this. Now the spec can change, of course, but also I want to make sure that the event we emit is the one that is most useful for analysis purposes.
For flows that end at this point, where the user never submits the form, it might be useful to know which form the user was engaged with when they abandoned the flow. How tricky is it to change this event name so that it is signup.engage or signin.engage? What about force_auth.engage?
vladikoff
Sep 15, 2016
Author
Contributor
Yea makes sense, I think my main issue is the word login, we don't use that anywhere. might be a good idea to stay away from that
Yea makes sense, I think my main issue is the word login, we don't use that anywhere. might be a good idea to stay away from that
| @@ -31,6 +31,8 @@ define(function (require, exports, module) { | |||
| */ | |||
| signUp: function (account, password) { | |||
| var self = this; | |||
| self.logEvent('flow.attempt_signup'); | |||
philbooth
Sep 15, 2016
Contributor
Does this correspond to signup.submit from the spec? Is there a reason to prefer this name over that one?
Does this correspond to signup.submit from the spec? Is there a reason to prefer this name over that one?
| if (DISABLE_CLIENT_METRICS_STDERR) { | ||
| return; | ||
| } | ||
|
|
||
| var events = metrics.events || []; | ||
| var hasFlowBeginEvent = events.some(function (event) { | ||
| var flowEvents = _.filter(events, function(event) { | ||
| return event.type.indexOf('flow.') === 0; |
philbooth
Sep 15, 2016
Contributor
...ahhh, and here I get my answer. The event names are changed because we use the flow. prefix as an indicator that it is a flow event.
Makes sense, but we still need to take into account the analysis of abandoned flows. Could we maybe change:
flow.engage to flow.signup.engage and flow.signin.engage?
flow.attempt_signup to flow.signup.submit?
flow.attempt_signin to flow.signin.submit?
...ahhh, and here I get my answer. The event names are changed because we use the flow. prefix as an indicator that it is a flow event.
Makes sense, but we still need to take into account the analysis of abandoned flows. Could we maybe change:
flow.engagetoflow.signup.engageandflow.signin.engage?flow.attempt_signuptoflow.signup.submit?flow.attempt_signintoflow.signin.submit?
| @@ -77,20 +82,14 @@ function optionallyLogFlowBeginEvent (req, metrics, requestReceivedTime) { | |||
| /*eslint-enable sorting/sort-object-props*/ | |||
| }); | |||
| } | |||
|
|
|||
| return true; | |||
philbooth
Sep 15, 2016
Contributor
Weird, I wonder why I did that?
Weird, I wonder why I did that?
| flow_id: metrics.flowId, //eslint-disable-line camelcase | ||
| flow_time: 0, //eslint-disable-line camelcase | ||
| time: metrics.flowBeginTime | ||
| }, req); | ||
| } | ||
| }); |
philbooth
Sep 15, 2016
Contributor
Not sure I understand the question?
Not sure I understand the question?
| @@ -24,13 +31,15 @@ define(function (require, exports, module) { | |||
| * @return {object} promise | |||
| */ | |||
| signIn: function (account, password) { | |||
| var self = this; | |||
| self.logEvent(`flow.${this.signInSubmitContext}.submit`); | |||
shane-tomlinson
Sep 16, 2016
Member
Is there a reason to use signInSubmitContext instead of this.viewName? Seems like the already existing name should suffice?
Is there a reason to use signInSubmitContext instead of this.viewName? Seems like the already existing name should suffice?
shane-tomlinson
Sep 16, 2016
Member
btw, all views have a viewName based off of their URL, if one is not already specified on the View's prototype.
btw, all views have a viewName based off of their URL, if one is not already specified on the View's prototype.
shane-tomlinson
Sep 16, 2016
Member
When will client side created flow events be used instead of server side created events? Up to this point, I thought all flow events were server side created.
When will client side created flow events be used instead of server side created events? Up to this point, I thought all flow events were server side created.
philbooth
Sep 16, 2016
•
Contributor
When will client side created flow events be used instead of server side created events?
Whenever the thing represented by the event occurs client-side.
The point of these events is to help us identify where users might be dropping off during the sign-in flow. Having extra events on the client, in between the existing flow.begin (client) and account.login (server) flow events provides a finer granularity for analysing those drop-offs. For instance, if some imaginary future UI change caused people to stop submitting the form for some reason, these events would help us identify that.
When will client side created flow events be used instead of server side created events?
Whenever the thing represented by the event occurs client-side.
The point of these events is to help us identify where users might be dropping off during the sign-in flow. Having extra events on the client, in between the existing flow.begin (client) and account.login (server) flow events provides a finer granularity for analysing those drop-offs. For instance, if some imaginary future UI change caused people to stop submitting the form for some reason, these events would help us identify that.
| @@ -121,6 +121,7 @@ define(function (require, exports, module) { | |||
|
|
|||
| this._marketingImpressions = {}; | |||
| this._activeExperiments = {}; | |||
| this._eventMemory = []; | |||
shane-tomlinson
Sep 16, 2016
•
Member
Like @rfk suggested, lookups could be a bit quicker by using an Object instead of an Array, though I don't imagine the list ever growing so long that lookups become a problem.
Another alternative is to skip storing a list of events locally and search this.events directly:
isEventLogged (eventName) {
return _.some(this.events, (event) => event.type === eventName);
}
Like @rfk suggested, lookups could be a bit quicker by using an Object instead of an Array, though I don't imagine the list ever growing so long that lookups become a problem.
Another alternative is to skip storing a list of events locally and search this.events directly:
isEventLogged (eventName) {
return _.some(this.events, (event) => event.type === eventName);
}
vladikoff
Sep 16, 2016
Author
Contributor
Another alternative is to skip storing a list of events locally and search this.events directly:
I don't think we can do that because those get flushed so it won't be logged once.
Otherwise an Object makes sense 👍
Another alternative is to skip storing a list of events locally and search this.events directly:
I don't think we can do that because those get flushed so it won't be logged once.
Otherwise an Object makes sense
| * | ||
| * @param {String} eventName | ||
| */ | ||
| logEventOnce: function (eventName) { |
shane-tomlinson
Sep 16, 2016
Member
One case to consider here is:
this.logEvent('eventName');
this.logEventOnce('eventName');
When logEventOnce is called, should it log an additional time? That seems a bit unexpected.
If you go with storing a list of events locally, I'd add the eventName to the list in logEvent and check whether it's already stored here.
One case to consider here is:
this.logEvent('eventName');
this.logEventOnce('eventName');When logEventOnce is called, should it log an additional time? That seems a bit unexpected.
If you go with storing a list of events locally, I'd add the eventName to the list in logEvent and check whether it's already stored here.
| * @param {String} eventName | ||
| */ | ||
| logEventOnce: function (eventName) { | ||
| if (_.indexOf(this._eventMemory, eventName) === -1) { |
shane-tomlinson
Sep 16, 2016
Member
See the previous comment about searching this.events directly instead of storing a local list.
See the previous comment about searching this.events directly instead of storing a local list.
| @@ -24,13 +31,15 @@ define(function (require, exports, module) { | |||
| * @return {object} promise | |||
| */ | |||
| signIn: function (account, password) { | |||
| var self = this; | |||
| self.logEvent(`flow.${this.signInSubmitContext}.submit`); | |||
shane-tomlinson
Sep 16, 2016
Member
When will client side created flow events be used instead of server side created events? Up to this point, I thought all flow events were server side created.
When will client side created flow events be used instead of server side created events? Up to this point, I thought all flow events were server side created.
|
Looks reasonable, though have a couple of questions/nits/suggestions. |
| @@ -31,6 +31,8 @@ define(function (require, exports, module) { | |||
| */ | |||
| signUp: function (account, password) { | |||
| var self = this; | |||
| self.logEvent('flow.signup.submit'); | |||
shane-tomlinson
Sep 16, 2016
Member
How will users that sign-in from the /signup page affect this stat?
How will users that sign-in from the /signup page affect this stat?
vladikoff
Sep 16, 2016
Author
Contributor
i think we talked irc about that but then the flow would be:
begin -> signup.engage -> signin.submit -> account.login
i think we talked irc about that but then the flow would be:
begin -> signup.engage -> signin.submit -> account.login
philbooth
Sep 16, 2016
Contributor
How will users that sign-in from the /signup page affect this stat?
Yep, just to confirm @vladikoff's answer, that is by design as per the feature doc.
How will users that sign-in from the /signup page affect this stat?
Yep, just to confirm @vladikoff's answer, that is by design as per the feature doc.
|
@philbooth final r? |
|
@vladikoff, LGTM, r+ |
Fixes #4119
TODO: tests@philbooth feedback?