Skip to content
This repository has been archived by the owner on Feb 29, 2020. It is now read-only.

Commit

Permalink
feat(metrics): #705 Collect proxy telemetries from addon
Browse files Browse the repository at this point in the history
  • Loading branch information
sarracini committed May 27, 2016
1 parent 0ef6ea7 commit d3535f2
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 11 deletions.
15 changes: 12 additions & 3 deletions lib/ActivityStreams.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function ActivityStreams(options = {}) {
this._telemetrySender = new TelemetrySender();
this._tabTracker = new TabTracker(this.appURLs, options.clientID, this._memoized);

this._previewProvider = new PreviewProvider();
this._previewProvider = new PreviewProvider(this._tabTracker);
this._populatingCache = {
places: false,
preview: false,
Expand Down Expand Up @@ -153,10 +153,12 @@ ActivityStreams.prototype = {
_processAndSendLinks(links, responseType, worker, options) {
let {append, previewsOnly, skipPreviewRequest} = options || {};
let processedLinks = this._previewProvider.processLinks(links);
const event = this._tabTracker.generateEvent({source: responseType});
if (!skipPreviewRequest) {
this._previewProvider.asyncSaveLinks(processedLinks);
this._previewProvider.asyncSaveLinks(processedLinks, event);
}
const cachedLinks = this._previewProvider.getEnhancedLinks(processedLinks, previewsOnly);
this._handlePerformanceEvent(event, processedLinks, cachedLinks);
this.send(am.actions.Response(responseType, cachedLinks, {append}), worker);
},

Expand Down Expand Up @@ -213,6 +215,12 @@ ActivityStreams.prototype = {
this._tabTracker.handleUserEvent(data.msg.data);
},

_handlePerformanceEvent(event, processedLinks, cachedLinks) {
this._tabTracker.handlePerformanceEvent(event, "previewCacheRequest", processedLinks.length);
this._tabTracker.handlePerformanceEvent(event, "previewCacheHits", cachedLinks.length);
this._tabTracker.handlePerformanceEvent(event, "previewCacheMisses", processedLinks.length - cachedLinks.length);
},

_onRouteChange(eventName, data) {
if (data) {
this._tabTracker.handleRouteChange(tabs.activeTab, data.msg.data);
Expand Down Expand Up @@ -356,8 +364,9 @@ ActivityStreams.prototype = {
}));

yield Promise.all(promises);
const event = this._tabTracker.generateEvent({source: "BUILD_PREVIEW_CACHE"});
linksToSend = this._previewProvider.processLinks(linksToSend);
yield this._previewProvider.asyncSaveLinks(linksToSend);
yield this._previewProvider.asyncSaveLinks(linksToSend, event);
this._populatingCache.preview = false;
Services.obs.notifyObservers(null, "activity-streams-previews-cache-complete", null);
}
Expand Down
23 changes: 17 additions & 6 deletions lib/PreviewProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ const DEFAULT_OPTIONS = {
initFresh: false,
};

function PreviewProvider(options = {}) {
function PreviewProvider(tabTracker, options = {}) {
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
this._onPrefChange = this._onPrefChange.bind(this);
this._tippyTopProvider = new TippyTopProvider();
this._tabTracker = tabTracker;
this.init();
this._runPeriodicUpdate();
}
Expand Down Expand Up @@ -225,7 +226,7 @@ PreviewProvider.prototype = {
/**
* Request links from embedly, optionally filtering out known links
*/
asyncSaveLinks: Task.async(function*(links, newOnly = true, updateAccessTime = true) {
asyncSaveLinks: Task.async(function*(links, event, newOnly = true, updateAccessTime = true) {

// optionally filter out known links, and links which already have a request in process
let linksList = this._uniqueLinks(links).filter(link => link && (!newOnly || !ss.storage.embedlyData[link.cacheKey]) && !this._alreadyRequested.has(link.cacheKey));
Expand All @@ -239,7 +240,7 @@ PreviewProvider.prototype = {
}
// for each bundle of 25 links, create a new request to embedly
requestQueue.forEach(requestBundle => {
promises.push(this._asyncFetchAndCache(requestBundle, updateAccessTime));
promises.push(this._asyncFetchAndCache(requestBundle, event, updateAccessTime));
});
yield Promise.all(promises);
}),
Expand All @@ -256,8 +257,9 @@ PreviewProvider.prototype = {
links.push(link);
}
}
const event = this._tabTracker.generateEvent({source: "UPDATE_LINKS"});
let linksToUpdate = this.processLinks(links);
yield this.asyncSaveLinks(linksToUpdate, false, false);
yield this.asyncSaveLinks(linksToUpdate, event, false, false);
Services.obs.notifyObservers(null, "activity-streams-preview-cache-update", null);
}),

Expand Down Expand Up @@ -292,17 +294,24 @@ PreviewProvider.prototype = {
/**
* Extracts data from embedly and caches it
*/
_asyncFetchAndCache: Task.async(function*(newLinks, updateAccessTime = true) {
_asyncFetchAndCache: Task.async(function*(newLinks, event, updateAccessTime = true) {
if (!this.enabled) {
return;
}
// extract only the sanitized link urls to send to embedly
let linkURLs = newLinks.map(link => link.sanitizedURL);
this._tabTracker.handlePerformanceEvent(event, "embedlyProxyRequestSentCount", newLinks.length);
try {
// Make network call when enabled
// Make network call when enabled and record how long the network call took
const startNetworkCall = Date.now();
let response = yield this._asyncGetLinkData(linkURLs);
const endNetworkCall = Date.now();
this._tabTracker.handlePerformanceEvent(event, "embedlyProxyRequestTime", endNetworkCall - startNetworkCall);

if (response.ok) {
let responseJson = yield response.json();
this._tabTracker.handlePerformanceEvent(event, "embedlyProxyRequestReceivedCount", responseJson.urls.length);
this._tabTracker.handlePerformanceEvent(event, "embedlyProxyRequestSucess", 1);
let currentTime = Date.now();
newLinks.forEach(link => {
let data = responseJson.urls[link.sanitizedURL];
Expand All @@ -315,6 +324,8 @@ PreviewProvider.prototype = {
}
ss.storage.embedlyData[link.cacheKey].refreshTime = currentTime;
});
} else {
this._tabTracker.handlePerformanceEvent(event, "embedlyProxyFailure", 1);
}
} catch (err) {
Cu.reportError(err);
Expand Down
16 changes: 16 additions & 0 deletions lib/TabTracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const tabs = require("sdk/tabs");
const {Cu} = require("chrome");
const self = require("sdk/self");
const {uuid} = require("sdk/util/uuid");
const simplePrefs = require("sdk/simple-prefs");
const eventConstants = require("../common/event-constants");

Expand All @@ -12,6 +13,7 @@ Cu.import("resource://gre/modules/Locale.jsm");
const TELEMETRY_PREF = "telemetry";
const COMPLETE_NOTIF = "tab-session-complete";
const ACTION_NOTIF = "user-action-event";
const PERFORMANCE_NOTIF = "performance-event";
const PERF_LOG_COMPLETE_NOTIF = "performance-log-complete";

function TabTracker(trackableURLs, clientID, placesQueries) {
Expand Down Expand Up @@ -106,13 +108,27 @@ TabTracker.prototype = {
}
},

handlePerformanceEvent(eventData, eventName, value) {
let payload = Object.assign({}, eventData);
payload.action = "activity_stream_performance";
payload.tab_id = tabs.activeTab.id;
payload.event = eventName;
payload.value = value;
this._setCommonProperties(payload, tabs.activeTab.url);
Services.obs.notifyObservers(null, PERFORMANCE_NOTIF, JSON.stringify(payload));
},

handleRouteChange(tab, route) {
if (!route.isFirstLoad) {
this.navigateAwayFromPage(tab, "route_change");
this.logReady(tab);
}
},

generateEvent(eventData) {
return Object.assign({}, eventData, {event_id: String(uuid())});
},

navigateAwayFromPage(tab, reason) {
// we can't use tab.url, because it's pointing to a new url of the page
// we have to use the URL stored in this._openTabs object
Expand Down
6 changes: 5 additions & 1 deletion lib/TelemetrySender.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Cu.importGlobalProperties(["fetch"]);
const ENDPOINT_PREF = "telemetry.ping.endpoint";
const TELEMETRY_PREF = "telemetry";
const ACTION_NOTIF = "user-action-event";
const PERFORMANCE_NOTIF = "performance-event";
const COMPLETE_NOTIF = "tab-session-complete";
const LOGGING_PREF = "performance.log";

Expand All @@ -22,12 +23,13 @@ function TelemetrySender() {
if (this.enabled) {
Services.obs.addObserver(this, COMPLETE_NOTIF);
Services.obs.addObserver(this, ACTION_NOTIF);
Services.obs.addObserver(this, PERFORMANCE_NOTIF);
}
}

TelemetrySender.prototype = {
observe(subject, topic, data) {
if (topic === COMPLETE_NOTIF || topic === ACTION_NOTIF) {
if (topic === COMPLETE_NOTIF || topic === ACTION_NOTIF || topic === PERFORMANCE_NOTIF) {
this._sendPing(data);
}
},
Expand All @@ -44,9 +46,11 @@ TelemetrySender.prototype = {
if (this.enabled && !newValue) {
Services.obs.removeObserver(this, COMPLETE_NOTIF);
Services.obs.removeObserver(this, ACTION_NOTIF);
Services.obs.removeObserver(this, PERFORMANCE_NOTIF);
} else if (!this.enabled && newValue) {
Services.obs.addObserver(this, COMPLETE_NOTIF);
Services.obs.addObserver(this, ACTION_NOTIF);
Services.obs.addObserver(this, PERFORMANCE_NOTIF);
}

this.enabled = newValue;
Expand Down
3 changes: 2 additions & 1 deletion test/test-PreviewProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,8 @@ exports.test_get_enhanced_previews_only = function*(assert) {
before(exports, function*() {
simplePrefs.prefs["embedly.endpoint"] = `http://localhost:${gPort}/embedlyLinkData`;
simplePrefs.prefs["previews.enabled"] = true;
gPreviewProvider = new PreviewProvider({initFresh: true});
let mockTabTracker = {handlePerformanceEvent: function() {}, generateEvent: function() {}};
gPreviewProvider = new PreviewProvider(mockTabTracker, {initFresh: true});
});

after(exports, function*() {
Expand Down
35 changes: 35 additions & 0 deletions test/test-TabTracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,41 @@ exports.test_TabTracker_unload_reason_with_user_action = function*(assert) {
}
};

exports.test_TabTracker_performance_action_pings = function*(assert) {
let performanceEventPromise = new Promise(resolve => {
function observe(subject, topic, data) {
if (topic === "performance-event") {
Services.obs.removeObserver(observe, "performance-event");
resolve(JSON.parse(data));
}
}
Services.obs.addObserver(observe, "performance-event");
});

let eventData1 = {
msg: {
data: {
source: "TOP_FRECENT_SITES_REQUEST",
event_id: "{c4f7e4a0-947b-7343-8a56-934c724492cc}",
event: "previewCacheHit",
value: 1
}
}
};
const event1 = app._tabTracker.generateEvent({source: "TOP_FRECENT_SITES_REQUEST"});
app._tabTracker.handlePerformanceEvent(event1, "previewCacheHit", 1);

let pingData = yield performanceEventPromise;
let additionalKeys = ["client_id", "addon_version", "locale", "action", "tab_id", "page"];
for (let key of additionalKeys) {
assert.ok(pingData[key], `The ping has the additional key ${key}`);
}
assert.ok(/{[0-9a-f\-]+}/.test(eventData1.msg.data.event_id), "ping has a UUID as an event ID");
assert.deepEqual(eventData1.msg.data.source, pingData.source, "the ping has the correct source");
assert.deepEqual(eventData1.msg.data.event, pingData.event, "the ping has the correct event");
assert.deepEqual(eventData1.msg.data.value, pingData.value, "the ping has the correct value");
};

exports.test_TabTracker_handleRouteChange_FirstLoad = function(assert) {
assert.deepEqual(app.tabData, {}, "tabData starts out empty");
app._tabTracker.handleRouteChange({}, {isFirstLoad: true});
Expand Down

0 comments on commit d3535f2

Please sign in to comment.