Bug 1552464 - Add telemetry probe to count the number of viewport changes in RDM. r=mtigley,nchevobbe.

This patch adds telemetry instrumentation to count the number of times the RDM viewport properties are changed (dimensions and rotation). This count will be correlated with the panel open count and time spent open to refine the baseline for RDM usage and filter out accidental usage.

A new Redux middleware, `telemetryMiddleware`, is introduced to the RDM Redux store. This observes actions dispatched to the store. For `RESIZE_VIEWPORT` and `ROTATE_VIEWPORT` actions, it increases a numeric value for the new scalar telemetry probe, `"devtools.responsive.viewport_change_count"`.

Other actions may be observed in this middleware for future telemetry instrumentation of RDM.

The `RESIZE_VIEWPORT` action is a dispatched with a high frequency when dragging to resize. Therefore, we debounce logging for this action. To ensure the test can reliably test counting this action without adding needless complexity to account for the asynchronicity, the `debounce()` utility is extended with an `immediate` parameter to cause the very first call to be executed immediately before going into the debounce behaviour.

Differential Revision: https://phabricator.services.mozilla.com/D31645

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Razvan Caliman 2019-05-24 08:52:41 +00:00
parent d05eeb5f3a
commit 49cc754a87
9 changed files with 138 additions and 5 deletions

View File

@ -40,7 +40,7 @@ const bootstrap = {
// toolbox session id.
this.telemetry.toolOpened("responsive", -1, this);
const store = this.store = Store();
const store = this.store = Store({ telemetry: this.telemetry });
const provider = createElement(Provider, { store }, App());
ReactDOM.render(provider, document.querySelector("#root"));
message.post(window, "init:done");

View File

@ -0,0 +1,8 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'telemetry.js',
)

View File

@ -0,0 +1,69 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { debounce } = require("devtools/shared/debounce");
const {
RESIZE_VIEWPORT,
ROTATE_VIEWPORT,
} = require("../actions/index");
const TELEMETRY_SCALAR_VIEWPORT_CHANGE_COUNT =
"devtools.responsive.viewport_change_count";
/**
* Redux middleware to observe actions dispatched to the Redux store and log to Telemetry.
*
* `telemetryMiddleware()` is a wrapper function which receives the Telemetry
* instance as its only argument and is used to create a closure to make that instance
* available to the returned Redux middleware and functions nested within.
*
* To be used with `applyMiddleware()` helper when creating the Redux store.
* @see https://redux.js.org/api/applymiddleware
*
* The wrapper returns the function that acts as the Redux middleware. This function
* receives a single `store` argument (the Redux Middleware API, an object containing
* `getState()` and `dispatch()`) and returns a function which receives a single `next`
* argument (the next middleware in the chain) which itself returns a function that
* receives a single `action` argument (the dispatched action).
*
* @param {Object} telemetry
* Instance of the Telemetry API
* @return {Function}
*/
function telemetryMiddleware(telemetry) {
function logViewportChange() {
telemetry.scalarAdd(TELEMETRY_SCALAR_VIEWPORT_CHANGE_COUNT, 1);
}
// Debounced logging to use in response to high frequency actions like RESIZE_VIEWPORT.
// Set debounce()'s `immediate` parameter to `true` to ensure the very first
// call is executed immediately. This helps the tests to check that logging is working
// without adding needless complexity to wait for the debounced call.
const logViewportChangeDebounced = debounce(logViewportChange, 300, null, true);
// This cascade of functions is the Redux middleware signature.
// @see https://redux.js.org/api/applymiddleware#arguments
return store => next => action => {
const res = next(action);
// Pass through to the next middleware if a telemetry instance is not available, for
// example when running unit tests.
if (!telemetry) {
return res;
}
switch (action.type) {
case ROTATE_VIEWPORT:
logViewportChange();
break;
case RESIZE_VIEWPORT:
logViewportChangeDebounced();
break;
}
return res;
};
}
module.exports = telemetryMiddleware;

View File

@ -9,6 +9,7 @@ DIRS += [
'browser',
'components',
'images',
'middleware',
'reducers',
'utils',
]

View File

@ -8,10 +8,12 @@ const { combineReducers } = require("devtools/client/shared/vendor/redux");
const createStore = require("devtools/client/shared/redux/create-store");
const reducers = require("./reducers");
const flags = require("devtools/shared/flags");
const telemetryMiddleware = require("./middleware/telemetry");
module.exports = function() {
module.exports = function(options = {}) {
let shouldLog = false;
let history;
const { telemetry } = options;
// If testing, store the action history in an array
// we'll later attach to the store
@ -23,6 +25,7 @@ module.exports = function() {
const store = createStore({
log: shouldLog,
history,
middleware: [telemetryMiddleware(telemetry)],
})(combineReducers(reducers), {});
if (history) {

View File

@ -64,6 +64,8 @@ skip-if = true # Bug 1413765
[browser_tab_remoteness_change.js]
[browser_target_blank.js]
[browser_telemetry_activate_rdm.js]
[browser_telemetry_viewport_change.js]
skip-if = (os == "mac" && debug)
[browser_toggle_zoom.js]
[browser_toolbox_computed_view.js]
[browser_toolbox_rule_view.js]

View File

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that actions to change the RDM viewport size and rotation are logged to telemetry.
*/
const TEST_URL = "data:text/html;charset=utf-8,browser_telemetry_viewport_change.js";
const TELEMETRY_SCALAR_VIEWPORT_CHANGE_COUNT =
"devtools.responsive.viewport_change_count";
addRDMTask(TEST_URL, async function({ ui, manager }) {
info("Clear any existing Telemetry scalars");
Services.telemetry.clearScalars();
info("Resize the viewport");
await setViewportSize(ui, manager, 100, 300);
info("Rotate the viewport");
rotateViewport(ui);
const scalars = Services.telemetry.getSnapshotForScalars("main", false);
ok(scalars.parent, "Telemetry scalars present");
const count = scalars.parent[TELEMETRY_SCALAR_VIEWPORT_CHANGE_COUNT];
is(count, 2, "Scalar has correct number of viewport changes logged");
});

View File

@ -14,9 +14,12 @@
* The wait period
* @param {Object} scope
* The scope to use for func
* @param {Boolean} immediate
* Whether to execute the method immediately on the first call.
* Optional. Default `false`.
* @return {Function} The debounced function
*/
exports.debounce = function(func, wait, scope) {
exports.debounce = function(func, wait, scope, immediate = false) {
let timer = null;
return function() {
@ -29,5 +32,10 @@ exports.debounce = function(func, wait, scope) {
timer = null;
func.apply(scope, args);
}, wait);
if (immediate) {
immediate = false;
func.apply(scope, args);
}
};
};

View File

@ -1570,7 +1570,7 @@ devtools.responsive:
kind: uint
notification_emails:
- dev-developer-tools@lists.mozilla.org
- jryans@mozilla.com
- mbalfanz@mozilla.com
record_in_processes:
- main
release_channel_collection: opt-out
@ -1584,7 +1584,20 @@ devtools.responsive:
keyed: true
notification_emails:
- dev-developer-tools@lists.mozilla.org
- jryans@mozilla.com
- mbalfanz@mozilla.com
record_in_processes:
- main
release_channel_collection: opt-out
viewport_change_count:
bug_numbers:
- 1552464
description: >
Number of times the viewport attributes changed while in Responsive Design Mode.
expires: never
kind: uint
notification_emails:
- dev-developer-tools@lists.mozilla.org
- mbalfanz@mozilla.com
record_in_processes:
- main
release_channel_collection: opt-out