summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsimonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-09 22:33:03 +0000
committersimonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-09 22:33:03 +0000
commitcd93f5fce8153bc8f0ac1ea178869169def1e0f9 (patch)
tree9be151c8771166e11f0aefc47bd1bab64947b207
parente98e3b9f2f4b89a74b1cc2b129108e170ae51f0f (diff)
downloadchromium_src-cd93f5fce8153bc8f0ac1ea178869169def1e0f9.zip
chromium_src-cd93f5fce8153bc8f0ac1ea178869169def1e0f9.tar.gz
chromium_src-cd93f5fce8153bc8f0ac1ea178869169def1e0f9.tar.bz2
The chromoting client logs connection statistics to the server.
BUG=106208 Review URL: http://codereview.chromium.org/8865005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113868 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/remoting.gyp1
-rw-r--r--remoting/webapp/me2mom/choice.html1
-rw-r--r--remoting/webapp/me2mom/client_screen.js4
-rw-r--r--remoting/webapp/me2mom/client_session.js43
-rw-r--r--remoting/webapp/me2mom/debug_log.js67
-rw-r--r--remoting/webapp/me2mom/log_to_server.js53
-rw-r--r--remoting/webapp/me2mom/server_log_entry.js174
-rw-r--r--remoting/webapp/me2mom/stats_accumulator.js126
8 files changed, 365 insertions, 104 deletions
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index d236613..427aed0 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -127,6 +127,7 @@
'webapp/me2mom/scale-to-fit.png',
'webapp/me2mom/server_log_entry.js',
'webapp/me2mom/spinner.gif',
+ 'webapp/me2mom/stats_accumulator.js',
'webapp/me2mom/toolbar.css',
'webapp/me2mom/toolbar.js',
'webapp/me2mom/ui_mode.js',
diff --git a/remoting/webapp/me2mom/choice.html b/remoting/webapp/me2mom/choice.html
index c7ba334..fa30d0d 100644
--- a/remoting/webapp/me2mom/choice.html
+++ b/remoting/webapp/me2mom/choice.html
@@ -28,6 +28,7 @@ found in the LICENSE file.
<script src="plugin_settings.js"></script>
<script src="remoting.js"></script>
<script src="server_log_entry.js"></script>
+ <script src="stats_accumulator.js"></script>
<script src="toolbar.js"></script>
<script src="ui_mode.js"></script>
<script src="util.js"></script>
diff --git a/remoting/webapp/me2mom/client_screen.js b/remoting/webapp/me2mom/client_screen.js
index 0090c53..1574e47 100644
--- a/remoting/webapp/me2mom/client_screen.js
+++ b/remoting/webapp/me2mom/client_screen.js
@@ -388,7 +388,9 @@ function updateStatistics_() {
remoting.clientSession.state != remoting.ClientSession.State.CONNECTED) {
return;
}
- remoting.debug.updateStatistics(remoting.clientSession.stats());
+ var stats = remoting.clientSession.stats();
+ remoting.debug.updateStatistics(stats);
+ remoting.clientSession.logStatistics(stats);
// Update the stats once per second.
window.setTimeout(updateStatistics_, 1000);
}
diff --git a/remoting/webapp/me2mom/client_session.js b/remoting/webapp/me2mom/client_session.js
index e9ae4bf..464aa50 100644
--- a/remoting/webapp/me2mom/client_session.js
+++ b/remoting/webapp/me2mom/client_session.js
@@ -68,6 +68,15 @@ remoting.ClientSession.ConnectionError = {
NETWORK_FAILURE: 4
};
+// Keys for connection statistics.
+remoting.ClientSession.STATS_KEY_VIDEO_BANDWIDTH = 'video_bandwidth';
+remoting.ClientSession.STATS_KEY_VIDEO_FRAME_RATE = 'video_frame_rate';
+remoting.ClientSession.STATS_KEY_CAPTURE_LATENCY = 'capture_latency';
+remoting.ClientSession.STATS_KEY_ENCODE_LATENCY = 'encode_latency';
+remoting.ClientSession.STATS_KEY_DECODE_LATENCY = 'decode_latency';
+remoting.ClientSession.STATS_KEY_RENDER_LATENCY = 'render_latency';
+remoting.ClientSession.STATS_KEY_ROUNDTRIP_LATENCY = 'roundtrip_latency';
+
/**
* The current state of the session.
* @type {remoting.ClientSession.State}
@@ -413,13 +422,29 @@ remoting.ClientSession.prototype.updateDimensions = function() {
* @return {Object.<string, number>} The connection statistics.
*/
remoting.ClientSession.prototype.stats = function() {
- return {
- 'video_bandwidth': this.plugin.videoBandwidth,
- 'video_frame_rate': this.plugin.videoFrameRate,
- 'capture_latency': this.plugin.videoCaptureLatency,
- 'encode_latency': this.plugin.videoEncodeLatency,
- 'decode_latency': this.plugin.videoDecodeLatency,
- 'render_latency': this.plugin.videoRenderLatency,
- 'roundtrip_latency': this.plugin.roundTripLatency
- };
+ var dict = {};
+ dict[remoting.ClientSession.STATS_KEY_VIDEO_BANDWIDTH] =
+ this.plugin.videoBandwidth;
+ dict[remoting.ClientSession.STATS_KEY_VIDEO_FRAME_RATE] =
+ this.plugin.videoFrameRate;
+ dict[remoting.ClientSession.STATS_KEY_CAPTURE_LATENCY] =
+ this.plugin.videoCaptureLatency;
+ dict[remoting.ClientSession.STATS_KEY_ENCODE_LATENCY] =
+ this.plugin.videoEncodeLatency;
+ dict[remoting.ClientSession.STATS_KEY_DECODE_LATENCY] =
+ this.plugin.videoDecodeLatency;
+ dict[remoting.ClientSession.STATS_KEY_RENDER_LATENCY] =
+ this.plugin.videoRenderLatency;
+ dict[remoting.ClientSession.STATS_KEY_ROUNDTRIP_LATENCY] =
+ this.plugin.roundTripLatency;
+ return dict;
+};
+
+/**
+ * Logs statistics.
+ *
+ * @param {Object.<string, number>} stats
+ */
+remoting.ClientSession.prototype.logStatistics = function(stats) {
+ this.logToServer.logStatistics(stats);
};
diff --git a/remoting/webapp/me2mom/debug_log.js b/remoting/webapp/me2mom/debug_log.js
index 3e0fd47..e60426d 100644
--- a/remoting/webapp/me2mom/debug_log.js
+++ b/remoting/webapp/me2mom/debug_log.js
@@ -90,7 +90,7 @@ remoting.DebugLog.prototype.toggle = function() {
*/
remoting.DebugLog.prototype.updateStatistics = function(stats) {
var units = '';
- var videoBandwidth = stats['video_bandwidth'];
+ var videoBandwidth = stats[remoting.ClientSession.STATS_KEY_VIDEO_BANDWIDTH];
if (videoBandwidth < 1024) {
units = 'Bps';
} else if (videoBandwidth < 1048576) {
@@ -108,13 +108,24 @@ remoting.DebugLog.prototype.updateStatistics = function(stats) {
this.statsElement.innerText =
'Bandwidth: ' + videoBandwidth.toFixed(2) + units +
', Frame Rate: ' +
- (stats['video_frame_rate'] ?
- stats['video_frame_rate'].toFixed(2) + ' fps' : 'n/a') +
- ', Capture: ' + stats['capture_latency'].toFixed(2) + 'ms' +
- ', Encode: ' + stats['encode_latency'].toFixed(2) + 'ms' +
- ', Decode: ' + stats['decode_latency'].toFixed(2) + 'ms' +
- ', Render: ' + stats['render_latency'].toFixed(2) + 'ms' +
- ', Latency: ' + stats['roundtrip_latency'].toFixed(2) + 'ms';
+ (stats[remoting.ClientSession.STATS_KEY_VIDEO_FRAME_RATE] ?
+ stats[remoting.ClientSession.STATS_KEY_VIDEO_FRAME_RATE].toFixed(2)
+ + ' fps' : 'n/a') +
+ ', Capture: ' +
+ stats[remoting.ClientSession.STATS_KEY_CAPTURE_LATENCY].toFixed(2) +
+ 'ms' +
+ ', Encode: ' +
+ stats[remoting.ClientSession.STATS_KEY_ENCODE_LATENCY].toFixed(2) +
+ 'ms' +
+ ', Decode: ' +
+ stats[remoting.ClientSession.STATS_KEY_DECODE_LATENCY].toFixed(2) +
+ 'ms' +
+ ', Render: ' +
+ stats[remoting.ClientSession.STATS_KEY_RENDER_LATENCY].toFixed(2) +
+ 'ms' +
+ ', Latency: ' +
+ stats[remoting.ClientSession.STATS_KEY_ROUNDTRIP_LATENCY].toFixed(2) +
+ 'ms';
};
/**
@@ -652,48 +663,10 @@ remoting.DebugLog.prototype.prettyIqSet = function(action, iq_list) {
return true;
}
}
- } else if (child.nodeName == 'gr:log') {
- var log = child;
- if (log.childNodes.length != 1) {
- return false;
- }
- if (!this.verifyAttributes(log, 'xmlns:gr')) {
- return false;
- }
-
- /** @type {Node} */
- var entry = log.childNodes[0];
- if (!this.verifyAttributes(entry, 'role,event-name,session-state,cpu,' +
- 'os-name,browser-version,webapp-version,id')) {
- return false;
- }
- var role = entry.getAttribute('role');
- if (role != 'client') {
- return false;
- }
- var event_name = entry.getAttribute('event-name');
- if (event_name != 'session-state') {
- return false;
- }
- var session_state = entry.getAttribute('session-state');
- this.prettyIqHeading(action, '?', 'log session-state ' + session_state,
- null);
-
- var os_name = entry.getAttribute('os-name');
- var cpu = entry.getAttribute('cpu');
- var browser_version = entry.getAttribute('browser-version');
- var webapp_version = entry.getAttribute('webapp-version');
- this.logIndent(1, os_name + ' ' + cpu + ' Chromium_v' + browser_version +
- ' Chromoting_v' + webapp_version);
- var remoting_id = entry.getAttribute('id');
- if (remoting_id) {
- this.logIndent(1, 'id: ' + remoting_id);
- }
- return true;
}
}
return false;
-}
+};
/**
* Print out an iq 'error'-type node.
diff --git a/remoting/webapp/me2mom/log_to_server.js b/remoting/webapp/me2mom/log_to_server.js
index 017bc1a..48f21b2 100644
--- a/remoting/webapp/me2mom/log_to_server.js
+++ b/remoting/webapp/me2mom/log_to_server.js
@@ -16,7 +16,10 @@ var remoting = remoting || {};
* @constructor
*/
remoting.LogToServer = function() {
- /** @type Array.<string> */ this.pendingEntries = [];
+ /** @type Array.<string> */
+ this.pendingEntries = [];
+ /** @type {remoting.StatsAccumulator} */
+ this.statsAccumulator = new remoting.StatsAccumulator();
};
// Local storage keys.
@@ -49,13 +52,50 @@ remoting.LogToServer.prototype.setEnabled = function(enabled) {
*/
remoting.LogToServer.prototype.logClientSessionStateChange =
function(state, connectionError) {
- var entry = remoting.ServerLogEntry.prototype.makeClientSessionStateChange(
+ var entry = remoting.ServerLogEntry.makeClientSessionStateChange(
state, connectionError);
entry.addHostFields();
entry.addChromeVersionField();
entry.addWebappVersionField();
entry.addIdField(this.getId());
this.log(entry);
+ // Don't accumulate connection statistics across state changes.
+ this.logAccumulatedStatistics();
+ this.statsAccumulator.empty();
+};
+
+/**
+ * Logs connection statistics.
+ * @param {Object.<string, number>} stats the connection statistics
+ */
+remoting.LogToServer.prototype.logStatistics = function(stats) {
+ // Store the statistics.
+ this.statsAccumulator.add(stats);
+ // Send statistics to the server if they've been accumulating for at least
+ // 60 seconds.
+ if (this.statsAccumulator.getTimeSinceFirstValue() >= 60 * 1000) {
+ this.logAccumulatedStatistics();
+ }
+};
+
+/**
+ * Moves connection statistics from the accumulator to the log server.
+ *
+ * If all the statistics are zero, then the accumulator is still emptied,
+ * but the statistics are not sent to the log server.
+ *
+ * @private
+ */
+remoting.LogToServer.prototype.logAccumulatedStatistics = function() {
+ var entry = remoting.ServerLogEntry.makeStats(this.statsAccumulator);
+ if (entry) {
+ entry.addHostFields();
+ entry.addChromeVersionField();
+ entry.addWebappVersionField();
+ entry.addIdField(this.getId());
+ this.log(entry);
+ }
+ this.statsAccumulator.empty();
};
/**
@@ -68,20 +108,25 @@ remoting.LogToServer.prototype.log = function(entry) {
if (!this.isEnabled()) {
return;
}
- // Store a stanza for the entry
+ // Send the stanza to the debug log.
+ remoting.debug.log('Enqueueing log entry:');
+ entry.toDebugLog(1);
+ // Store a stanza for the entry.
this.pendingEntries.push(entry.toStanza());
// Stop if there's no connection to the server.
if (!remoting.wcs) {
return;
}
// Send all pending entries to the server.
+ remoting.debug.log('Sending ' + this.pendingEntries.length + ' log ' +
+ ((this.pendingEntries.length == 1) ? 'entry' : 'entries') +
+ ' to the server.');
var stanza = '<cli:iq to="remoting@bot.talk.google.com" type="set" ' +
'xmlns:cli="jabber:client"><gr:log xmlns:gr="google:remoting">';
while (this.pendingEntries.length > 0) {
stanza += /** @type string */ this.pendingEntries.shift();
}
stanza += '</gr:log></cli:iq>';
- remoting.debug.logIq(true, stanza);
remoting.wcs.sendIq(stanza);
};
diff --git a/remoting/webapp/me2mom/server_log_entry.js b/remoting/webapp/me2mom/server_log_entry.js
index 892a91a..a8755b9 100644
--- a/remoting/webapp/me2mom/server_log_entry.js
+++ b/remoting/webapp/me2mom/server_log_entry.js
@@ -21,28 +21,28 @@ remoting.ServerLogEntry = function() {
};
/** @private */
-remoting.ServerLogEntry.prototype.KEY_EVENT_NAME_ = 'event-name';
+remoting.ServerLogEntry.KEY_EVENT_NAME_ = 'event-name';
/** @private */
-remoting.ServerLogEntry.prototype.VALUE_EVENT_NAME_SESSION_STATE_ =
+remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_STATE_ =
'session-state';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_ID_ = 'id';
+remoting.ServerLogEntry.KEY_ID_ = 'id';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_ROLE_ = 'role';
+remoting.ServerLogEntry.KEY_ROLE_ = 'role';
/** @private */
-remoting.ServerLogEntry.prototype.VALUE_ROLE_CLIENT_ = 'client';
+remoting.ServerLogEntry.VALUE_ROLE_CLIENT_ = 'client';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_SESSION_STATE_ = 'session-state';
+remoting.ServerLogEntry.KEY_SESSION_STATE_ = 'session-state';
/**
* @private
* @param {remoting.ClientSession.State} state
* @return {string}
*/
-remoting.ServerLogEntry.prototype.getValueForSessionState = function(state) {
+remoting.ServerLogEntry.getValueForSessionState = function(state) {
switch(state) {
case remoting.ClientSession.State.UNKNOWN:
return 'unknown';
@@ -68,14 +68,14 @@ remoting.ServerLogEntry.prototype.getValueForSessionState = function(state) {
};
/** @private */
-remoting.ServerLogEntry.prototype.KEY_CONNECTION_ERROR_ = 'connection-error';
+remoting.ServerLogEntry.KEY_CONNECTION_ERROR_ = 'connection-error';
/**
* @private
* @param {remoting.ClientSession.ConnectionError} connectionError
* @return {string}
*/
-remoting.ServerLogEntry.prototype.getValueForConnectionError =
+remoting.ServerLogEntry.getValueForConnectionError =
function(connectionError) {
switch(connectionError) {
case remoting.ClientSession.ConnectionError.NONE:
@@ -94,27 +94,43 @@ remoting.ServerLogEntry.prototype.getValueForConnectionError =
}
/** @private */
-remoting.ServerLogEntry.prototype.KEY_OS_NAME_ = 'os-name';
+remoting.ServerLogEntry.VALUE_EVENT_NAME_CONNECTION_STATISTICS_ =
+ "connection-statistics";
/** @private */
-remoting.ServerLogEntry.prototype.VALUE_OS_NAME_WINDOWS_ = 'Windows';
+remoting.ServerLogEntry.KEY_VIDEO_BANDWIDTH_ = "video-bandwidth";
/** @private */
-remoting.ServerLogEntry.prototype.VALUE_OS_NAME_LINUX_ = 'Linux';
+remoting.ServerLogEntry.KEY_CAPTURE_LATENCY_ = "capture-latency";
/** @private */
-remoting.ServerLogEntry.prototype.VALUE_OS_NAME_MAC_ = 'Mac';
+remoting.ServerLogEntry.KEY_ENCODE_LATENCY_ = "encode-latency";
/** @private */
-remoting.ServerLogEntry.prototype.VALUE_OS_NAME_CHROMEOS_ = 'ChromeOS';
+remoting.ServerLogEntry.KEY_DECODE_LATENCY_ = "decode-latency";
+/** @private */
+remoting.ServerLogEntry.KEY_RENDER_LATENCY_ = "render-latency";
+/** @private */
+remoting.ServerLogEntry.KEY_ROUNDTRIP_LATENCY_ = "roundtrip-latency";
+
+/** @private */
+remoting.ServerLogEntry.KEY_OS_NAME_ = 'os-name';
+/** @private */
+remoting.ServerLogEntry.VALUE_OS_NAME_WINDOWS_ = 'Windows';
+/** @private */
+remoting.ServerLogEntry.VALUE_OS_NAME_LINUX_ = 'Linux';
+/** @private */
+remoting.ServerLogEntry.VALUE_OS_NAME_MAC_ = 'Mac';
+/** @private */
+remoting.ServerLogEntry.VALUE_OS_NAME_CHROMEOS_ = 'ChromeOS';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_OS_VERSION_ = 'os-version';
+remoting.ServerLogEntry.KEY_OS_VERSION_ = 'os-version';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_CPU_ = 'cpu';
+remoting.ServerLogEntry.KEY_CPU_ = 'cpu';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_BROWSER_VERSION_ = 'browser-version';
+remoting.ServerLogEntry.KEY_BROWSER_VERSION_ = 'browser-version';
/** @private */
-remoting.ServerLogEntry.prototype.KEY_WEBAPP_VERSION_ = 'webapp-version';
+remoting.ServerLogEntry.KEY_WEBAPP_VERSION_ = 'webapp-version';
/**
* Sets one field in this log entry.
@@ -142,48 +158,119 @@ remoting.ServerLogEntry.prototype.toStanza = function() {
};
/**
+ * Prints this object on the debug log.
+ *
+ * @param {number} indentLevel the indentation level
+ */
+remoting.ServerLogEntry.prototype.toDebugLog = function(indentLevel) {
+ /** @type Array.<string> */ var fields = [];
+ for (var key in this.dict) {
+ fields.push(key + ': ' + this.dict[key]);
+ }
+ remoting.debug.logIndent(indentLevel, fields.join(', '));
+};
+
+/**
* Makes a log entry for a change of client session state.
*
* @param {remoting.ClientSession.State} state
* @param {remoting.ClientSession.ConnectionError} connectionError
* @return {remoting.ServerLogEntry}
*/
-remoting.ServerLogEntry.prototype.makeClientSessionStateChange =
- function(state, connectionError) {
+remoting.ServerLogEntry.makeClientSessionStateChange = function(state,
+ connectionError) {
var entry = new remoting.ServerLogEntry();
- entry.set(this.KEY_ROLE_, this.VALUE_ROLE_CLIENT_);
- entry.set(this.KEY_EVENT_NAME_, this.VALUE_EVENT_NAME_SESSION_STATE_);
- entry.set(this.KEY_SESSION_STATE_, this.getValueForSessionState(state));
+ entry.set(remoting.ServerLogEntry.KEY_ROLE_,
+ remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
+ entry.set(remoting.ServerLogEntry.KEY_EVENT_NAME_,
+ remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_STATE_);
+ entry.set(remoting.ServerLogEntry.KEY_SESSION_STATE_,
+ remoting.ServerLogEntry.getValueForSessionState(state));
if (connectionError != remoting.ClientSession.ConnectionError.NONE) {
- entry.set(this.KEY_CONNECTION_ERROR_,
- this.getValueForConnectionError(connectionError));
+ entry.set(remoting.ServerLogEntry.KEY_CONNECTION_ERROR_,
+ remoting.ServerLogEntry.getValueForConnectionError(
+ connectionError));
}
return entry;
};
/**
+ * Makes a log entry for a set of connection statistics.
+ * Returns null if all the statistics were zero.
+ *
+ * @param {remoting.StatsAccumulator} statsAccumulator
+ * @return {?remoting.ServerLogEntry}
+ */
+remoting.ServerLogEntry.makeStats = function(statsAccumulator) {
+ var entry = new remoting.ServerLogEntry();
+ entry.set(remoting.ServerLogEntry.KEY_ROLE_,
+ remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
+ entry.set(remoting.ServerLogEntry.KEY_EVENT_NAME_,
+ remoting.ServerLogEntry.VALUE_EVENT_NAME_CONNECTION_STATISTICS_);
+ var nonZero = false;
+ nonZero |= entry.addStatsField(
+ remoting.ServerLogEntry.KEY_VIDEO_BANDWIDTH_,
+ remoting.ClientSession.STATS_KEY_VIDEO_BANDWIDTH, statsAccumulator);
+ nonZero |= entry.addStatsField(
+ remoting.ServerLogEntry.KEY_CAPTURE_LATENCY_,
+ remoting.ClientSession.STATS_KEY_CAPTURE_LATENCY, statsAccumulator);
+ nonZero |= entry.addStatsField(
+ remoting.ServerLogEntry.KEY_ENCODE_LATENCY_,
+ remoting.ClientSession.STATS_KEY_ENCODE_LATENCY, statsAccumulator);
+ nonZero |= entry.addStatsField(
+ remoting.ServerLogEntry.KEY_DECODE_LATENCY_,
+ remoting.ClientSession.STATS_KEY_DECODE_LATENCY, statsAccumulator);
+ nonZero |= entry.addStatsField(
+ remoting.ServerLogEntry.KEY_RENDER_LATENCY_,
+ remoting.ClientSession.STATS_KEY_RENDER_LATENCY, statsAccumulator);
+ nonZero |= entry.addStatsField(
+ remoting.ServerLogEntry.KEY_ROUNDTRIP_LATENCY_,
+ remoting.ClientSession.STATS_KEY_ROUNDTRIP_LATENCY, statsAccumulator);
+ if (nonZero) {
+ return entry;
+ }
+ return null;
+};
+
+/**
+ * Adds one connection statistic to a log entry.
+ *
+ * @private
+ * @param {string} entryKey
+ * @param {string} statsKey
+ * @param {remoting.StatsAccumulator} statsAccumulator
+ * @return {boolean} whether the statistic is non-zero
+ */
+remoting.ServerLogEntry.prototype.addStatsField = function(
+ entryKey, statsKey, statsAccumulator) {
+ var val = statsAccumulator.calcMean(statsKey);
+ this.set(entryKey, val.toString());
+ return (val != 0);
+};
+
+/**
* Adds an ID field to this log entry.
*
* @param {string} id
*/
remoting.ServerLogEntry.prototype.addIdField = function(id) {
- this.set(this.KEY_ID_, id);
+ this.set(remoting.ServerLogEntry.KEY_ID_, id);
}
/**
* Adds fields describing the host to this log entry.
*/
remoting.ServerLogEntry.prototype.addHostFields = function() {
- var host = this.getHostData();
+ var host = remoting.ServerLogEntry.getHostData();
if (host) {
if (host.os_name.length > 0) {
- this.set(this.KEY_OS_NAME_, host.os_name);
+ this.set(remoting.ServerLogEntry.KEY_OS_NAME_, host.os_name);
}
if (host.os_version.length > 0) {
- this.set(this.KEY_OS_VERSION_, host.os_version);
+ this.set(remoting.ServerLogEntry.KEY_OS_VERSION_, host.os_version);
}
if (host.cpu.length > 0) {
- this.set(this.KEY_CPU_, host.cpu);
+ this.set(remoting.ServerLogEntry.KEY_CPU_, host.cpu);
}
}
};
@@ -194,8 +281,8 @@ remoting.ServerLogEntry.prototype.addHostFields = function() {
* @private
* @return {{os_name:string, os_version:string, cpu:string} | null}
*/
-remoting.ServerLogEntry.prototype.getHostData = function() {
- return this.extractHostDataFrom(navigator.userAgent);
+remoting.ServerLogEntry.getHostData = function() {
+ return remoting.ServerLogEntry.extractHostDataFrom(navigator.userAgent);
};
/**
@@ -205,7 +292,7 @@ remoting.ServerLogEntry.prototype.getHostData = function() {
* @param {string} s
* @return {{os_name:string, os_version:string, cpu:string} | null}
*/
-remoting.ServerLogEntry.prototype.extractHostDataFrom = function(s) {
+remoting.ServerLogEntry.extractHostDataFrom = function(s) {
// Sample userAgent strings:
// 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 ' +
// '(KHTML, like Gecko) Chrome/15.0.874.106 Safari/535.2'
@@ -218,7 +305,7 @@ remoting.ServerLogEntry.prototype.extractHostDataFrom = function(s) {
var match = new RegExp('Windows NT ([0-9\\.]*)').exec(s);
if (match && (match.length >= 2)) {
return {
- 'os_name': this.VALUE_OS_NAME_WINDOWS_,
+ 'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_WINDOWS_,
'os_version': match[1],
'cpu': ''
};
@@ -226,7 +313,7 @@ remoting.ServerLogEntry.prototype.extractHostDataFrom = function(s) {
match = new RegExp('Linux ([a-zA-Z0-9_]*)').exec(s);
if (match && (match.length >= 2)) {
return {
- 'os_name': this.VALUE_OS_NAME_LINUX_,
+ 'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_LINUX_,
'os_version' : '',
'cpu': match[1]
};
@@ -234,7 +321,7 @@ remoting.ServerLogEntry.prototype.extractHostDataFrom = function(s) {
match = new RegExp('([a-zA-Z]*) Mac OS X ([0-9_]*)').exec(s);
if (match && (match.length >= 3)) {
return {
- 'os_name': this.VALUE_OS_NAME_MAC_,
+ 'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_MAC_,
'os_version': match[2].replace(/_/g, '.'),
'cpu': match[1]
};
@@ -242,7 +329,7 @@ remoting.ServerLogEntry.prototype.extractHostDataFrom = function(s) {
match = new RegExp('CrOS ([a-zA-Z0-9]*) ([0-9.]*)').exec(s);
if (match && (match.length >= 3)) {
return {
- 'os_name': this.VALUE_OS_NAME_CHROMEOS_,
+ 'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_CHROMEOS_,
'os_version': match[2],
'cpu': match[1]
};
@@ -254,9 +341,9 @@ remoting.ServerLogEntry.prototype.extractHostDataFrom = function(s) {
* Adds a field specifying the browser version to this log entry.
*/
remoting.ServerLogEntry.prototype.addChromeVersionField = function() {
- var version = this.getChromeVersion();
+ var version = remoting.ServerLogEntry.getChromeVersion();
if (version != null) {
- this.set(this.KEY_BROWSER_VERSION_, version);
+ this.set(remoting.ServerLogEntry.KEY_BROWSER_VERSION_, version);
}
};
@@ -266,8 +353,8 @@ remoting.ServerLogEntry.prototype.addChromeVersionField = function() {
* @private
* @return {string | null}
*/
-remoting.ServerLogEntry.prototype.getChromeVersion = function() {
- return this.extractChromeVersionFrom(navigator.userAgent);
+remoting.ServerLogEntry.getChromeVersion = function() {
+ return remoting.ServerLogEntry.extractChromeVersionFrom(navigator.userAgent);
};
/**
@@ -277,7 +364,7 @@ remoting.ServerLogEntry.prototype.getChromeVersion = function() {
* @param {string} s
* @return {string | null}
*/
-remoting.ServerLogEntry.prototype.extractChromeVersionFrom = function(s) {
+remoting.ServerLogEntry.extractChromeVersionFrom = function(s) {
var match = new RegExp('Chrome/([0-9.]*)').exec(s);
if (match && (match.length >= 2)) {
return match[1];
@@ -289,5 +376,6 @@ remoting.ServerLogEntry.prototype.extractChromeVersionFrom = function(s) {
* Adds a field specifying the webapp version to this log entry.
*/
remoting.ServerLogEntry.prototype.addWebappVersionField = function() {
- this.set(this.KEY_WEBAPP_VERSION_, chrome.app.getDetails().version);
+ this.set(remoting.ServerLogEntry.KEY_WEBAPP_VERSION_,
+ chrome.app.getDetails().version);
};
diff --git a/remoting/webapp/me2mom/stats_accumulator.js b/remoting/webapp/me2mom/stats_accumulator.js
new file mode 100644
index 0000000..adf89c5
--- /dev/null
+++ b/remoting/webapp/me2mom/stats_accumulator.js
@@ -0,0 +1,126 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * The webapp reads the plugin's connection statistics frequently (once per
+ * second). It logs statistics to the server less frequently, to keep
+ * bandwidth and storage costs down. This class bridges that gap, by
+ * accumulating high-frequency numeric data, and providing statistics
+ * summarising that data.
+ */
+
+'use strict';
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+/**
+ * @constructor
+ */
+remoting.StatsAccumulator = function() {
+ /**
+ * A map from names to lists of values.
+ * @private
+ * @type Object.<string, Array.<number>>
+ */
+ this.valueLists_ = {};
+
+ /**
+ * The first time, after this object was most recently initialized or emptied,
+ * at which a value was added to this object.
+ * @private
+ * @type {?number}
+ */
+ this.timeOfFirstValue_ = null;
+};
+
+/**
+ * Adds values to this object.
+ *
+ * @param {Object.<string, number>} newValues
+ */
+remoting.StatsAccumulator.prototype.add = function(newValues) {
+ for (var key in newValues) {
+ this.getValueList(key).push(newValues[key]);
+ }
+ if (!this.timeOfFirstValue_) {
+ this.timeOfFirstValue_ = new Date().getTime();
+ }
+};
+
+/**
+ * Empties this object.
+ */
+remoting.StatsAccumulator.prototype.empty = function() {
+ this.valueLists_ = {};
+ this.timeOfFirstValue_ = null;
+};
+
+/**
+ * Gets the number of milliseconds since the first value was added to this
+ * object, after this object was most recently initialized or emptied.
+ *
+ * @return {number} milliseconds since the first value
+ */
+remoting.StatsAccumulator.prototype.getTimeSinceFirstValue = function() {
+ if (!this.timeOfFirstValue_) {
+ return 0;
+ }
+ return new Date().getTime() - this.timeOfFirstValue_;
+};
+
+/**
+ * Calculates the mean of the values for a given key.
+ *
+ * @param {string} key
+ * @return {number} the mean of the values for that key
+ */
+remoting.StatsAccumulator.prototype.calcMean = function(key) {
+ /**
+ * @param {Array.<number>} values
+ * @return {number}
+ */
+ var calcMean = function(values) {
+ if (values.length == 0) {
+ return 0.0;
+ }
+ var sum = 0;
+ for (var i = 0; i < values.length; i++) {
+ sum += values[i];
+ }
+ return sum / values.length;
+ };
+ return this.map(key, calcMean);
+};
+
+/**
+ * Applies a given map to the list of values for a given key.
+ *
+ * @param {string} key
+ * @param {function(Array.<number>): number} map
+ * @return {number} the result of applying that map to the list of values for
+ * that key
+ */
+remoting.StatsAccumulator.prototype.map = function(key, map) {
+ return map(this.getValueList(key));
+};
+
+/**
+ * Gets the list of values for a given key.
+ * If this object contains no values for that key, then this routine creates
+ * an empty list, stores it in this object, and returns it.
+ *
+ * @private
+ * @param {string} key
+ * @return {Array.<number>} the list of values for that key
+ */
+remoting.StatsAccumulator.prototype.getValueList = function(key) {
+ var valueList = this.valueLists_[key];
+ if (!valueList) {
+ valueList = [];
+ this.valueLists_[key] = valueList;
+ }
+ return valueList;
+};