Bug 998989 - save as telemetry ping file for upload by telemetry. r=vladan

This commit is contained in:
Jan-Ivar Bruaroey 2014-07-10 12:33:13 -04:00
parent fc48e8c4ac
commit f537c69ad6
3 changed files with 106 additions and 46 deletions

View File

@ -9,6 +9,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/osfile.jsm", this);
this.EXPORTED_SYMBOLS = ["MozLoopService"];
@ -52,7 +53,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
let MozLoopServiceInternal = {
// The uri of the Loop server.
loopServerUri: Services.prefs.getCharPref("loop.server"),
telemetryUri: Services.prefs.getCharPref("loop.telemetryURL"),
// The current deferred for the registration process. This is set if in progress
// or the registration was successful. This is null if a registration attempt was
@ -314,65 +314,67 @@ let MozLoopServiceInternal = {
},
/**
* Uploads telemetry logs to telemetryServer
* Saves loop logs to the saved-telemetry-pings folder.
*
* @param {Object} pc The peerConnection in question.
*/
uploadTelemetry: function(window, pc) {
if (!this.telemetryUri) {
return;
}
stageForTelemetryUpload: function(window, pc) {
window.WebrtcGlobalInformation.getAllStats(allStats => {
let internalFormat = allStats.reports[0]; // filtered on pc.id
window.WebrtcGlobalInformation.getLogging('', logs => {
let report = convertToRTCStatsReport(internalFormat);
let logStr = "";
logs.forEach(s => { logStr += s + "\n"; });
// We have stats and logs.
// Prepare payload from https://wiki.mozilla.org/Loop/Telemetry
// Create worker job. ping = saved telemetry ping file header + payload
//
// Prepare payload according to https://wiki.mozilla.org/Loop/Telemetry
let ai = Services.appinfo;
let report = convertToRTCStatsReport(internalFormat);
let payload = {
ver: 1,
info: {
appUpdateChannel: ai.defaultUpdateChannel,
appBuildID: ai.appBuildID,
appName: ai.name,
appVersion: ai.version,
reason: "loop",
OS: ai.OS,
version: Services.sysinfo.getProperty("version")
},
report: "ice failure",
connectionstate: pc.iceConnectionState,
stats: report,
localSdp: internalFormat.localSdp,
remoteSdp: internalFormat.remoteSdp,
log: ""
};
logs.forEach(s => { payload.log += s + "\n"; });
let uuid = uuidgen.generateUUID().toString();
uuid = uuid.substr(1,uuid.length-2); // remove uuid curly braces
let url = this.telemetryUri;
url += ((url.substr(-1) == "/")? "":"/") + uuid + "/loop/" +
ai.OS + "/" + ai.version + "/" + ai.defaultUpdateChannel + "/" +
ai.appBuildID;
// Send payload.
//TODO: gzip!
let xhr = new window.XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log("Failed to upload telemetry logs: " + xhr.responseText);
let directory = OS.Path.join(OS.Constants.Path.profileDir,
"saved-telemetry-pings");
let job = {
directory: directory,
filename: uuid + ".json",
ping: {
reason: "loop",
slug: uuid,
payload: {
ver: 1,
info: {
appUpdateChannel: ai.defaultUpdateChannel,
appBuildID: ai.appBuildID,
appName: ai.name,
appVersion: ai.version,
reason: "loop",
OS: ai.OS,
version: Services.sysinfo.getProperty("version")
},
report: "ice failure",
connectionstate: pc.iceConnectionState,
stats: report,
localSdp: internalFormat.localSdp,
remoteSdp: internalFormat.remoteSdp,
log: logStr
}
}
};
xhr.send(JSON.stringify(payload));
console.log("Uploading telemetry logs to " + url);
// Send job to worker to do saving to
// disk for pickup by telemetry on next startup, which then uploads it.
let worker = new ChromeWorker("MozLoopWorker.js");
worker.onmessage = function(e) {
console.log(e.data.ok ?
"Successfully staged loop report for telemetry upload." :
("Failed to stage loop report. Error: " + e.data.fail));
}
worker.postMessage(job);
});
}, pc.id);
},
@ -420,7 +422,10 @@ let MozLoopServiceInternal = {
switch(pc.iceConnectionState) {
case "failed":
case "disconnected":
this.uploadTelemetry(window, pc);
if (Services.telemetry.canSend ||
Services.prefs.getBoolPref("toolkit.telemetry.test")) {
this.stageForTelemetryUpload(window, pc);
}
break;
}
}

View File

@ -0,0 +1,54 @@
/* 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/. */
/**
* A worker dedicated to loop-report sanitation and writing for MozLoopService.
*/
"use strict";
importScripts("resource://gre/modules/osfile.jsm");
let File = OS.File;
let Encoder = new TextEncoder();
let Counter = 0;
const MAX_LOOP_LOGS = 5;
/**
* Communications with the controller.
*
* Accepts messages:
* { path: filepath, ping: data }
*
* Sends messages:
* { ok: true }
* { fail: serialized_form_of_OS.File.Error }
*/
onmessage = function(e) {
if (++Counter > MAX_LOOP_LOGS) {
postMessage({
fail: "Maximum " + MAX_LOOP_LOGS + "loop reports reached for this session"
});
return;
}
let directory = e.data.directory;
let filename = e.data.filename;
let ping = e.data.ping;
let pingStr = JSON.stringify(ping);
// Save to disk
let array = Encoder.encode(pingStr);
try {
File.makeDir(directory,
{ unixMode: OS.Constants.S_IRWXU, ignoreExisting: true });
File.writeAtomic(OS.Path.join(directory, filename), array);
postMessage({ ok: true });
} catch (ex if ex instanceof File.Error) {
// Instances of OS.File.Error know how to serialize themselves
postMessage({fail: File.Error.toMsg(ex)});
}
};

View File

@ -14,4 +14,5 @@ EXTRA_JS_MODULES += [
'MozLoopAPI.jsm',
'MozLoopPushHandler.jsm',
'MozLoopService.jsm',
'MozLoopWorker.js',
]