Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview | |
| 7 * Module for sending log entries to the server. | |
| 8 */ | |
| 9 | |
| 10 'use strict'; | |
| 11 | |
| 12 /** @suppress {duplicate} */ | |
| 13 var remoting = remoting || {}; | |
| 14 | |
| 15 /** | |
| 16 * @constructor | |
| 17 */ | |
| 18 remoting.LogToServer = function() { | |
| 19 /** @type Array.<string> */ this.pendingEntries = []; | |
| 20 }; | |
| 21 | |
| 22 remoting.LogToServer.prototype.KEY_EVENT_NAME = 'event-name'; | |
| 23 remoting.LogToServer.prototype.VALUE_EVENT_NAME_SESSION_STATE = 'session-state'; | |
|
Jamie
2011/11/10 23:27:43
Is it worth tagging these as @private (and appendi
simonmorris
2011/11/11 17:11:23
Done.
| |
| 24 | |
| 25 remoting.LogToServer.prototype.KEY_ROLE = 'role'; | |
| 26 remoting.LogToServer.prototype.VALUE_ROLE_CLIENT = 'client'; | |
| 27 | |
| 28 remoting.LogToServer.prototype.KEY_SESSION_STATE = 'session-state'; | |
| 29 | |
| 30 /** | |
| 31 * @param {remoting.ClientSession.State} state | |
| 32 * @return {string} | |
| 33 */ | |
| 34 remoting.LogToServer.prototype.VALUE_SESSION_STATE = function(state) { | |
|
Jamie
2011/11/10 23:27:43
I worry about having to remember to keep this tran
simonmorris
2011/11/11 17:11:23
I've added a number in the undefined case. But if
| |
| 35 switch(state) { | |
| 36 case remoting.ClientSession.State.UNKNOWN: | |
| 37 return 'unknown'; | |
| 38 case remoting.ClientSession.State.CREATED: | |
| 39 return 'created'; | |
| 40 case remoting.ClientSession.State.BAD_PLUGIN_VERSION: | |
| 41 return 'bad-plugin-version'; | |
| 42 case remoting.ClientSession.State.UNKNOWN_PLUGIN_ERROR: | |
| 43 return 'unknown-plugin-error'; | |
| 44 case remoting.ClientSession.State.CONNECTING: | |
| 45 return 'connecting'; | |
| 46 case remoting.ClientSession.State.INITIALIZING: | |
| 47 return 'initializing'; | |
| 48 case remoting.ClientSession.State.CONNECTED: | |
| 49 return 'connected'; | |
| 50 case remoting.ClientSession.State.CLOSED: | |
| 51 return 'closed'; | |
| 52 case remoting.ClientSession.State.CONNECTION_FAILED: | |
| 53 return 'connection-failed'; | |
| 54 default: | |
| 55 return 'undefined'; | |
| 56 } | |
| 57 }; | |
| 58 | |
| 59 remoting.LogToServer.prototype.KEY_CONNECTION_ERROR = 'connection-error'; | |
| 60 | |
| 61 /** | |
| 62 * @param {remoting.ClientSession.ConnectionError} connectionError | |
| 63 * @return {string} | |
| 64 */ | |
| 65 remoting.LogToServer.prototype.VALUE_CONNECTION_ERROR = | |
|
Jamie
2011/11/10 23:27:43
Ditto for this function.
simonmorris
2011/11/11 17:11:23
Done.
| |
| 66 function(connectionError) { | |
| 67 switch(connectionError) { | |
| 68 case remoting.ClientSession.ConnectionError.NONE: | |
| 69 return 'none'; | |
| 70 case remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE: | |
| 71 return 'host-is-offline'; | |
| 72 case remoting.ClientSession.ConnectionError.SESSION_REJECTED: | |
| 73 return 'session-rejected'; | |
| 74 case remoting.ClientSession.ConnectionError.INCOMPATIBLE_PROTOCOL: | |
| 75 return 'incompatible-protocol'; | |
| 76 case remoting.ClientSession.ConnectionError.NETWORK_FAILURE: | |
| 77 return 'network-failure'; | |
| 78 case remoting.ClientSession.ConnectionError.OTHER: | |
| 79 return 'other'; | |
| 80 default: | |
| 81 return 'unknown'; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 remoting.LogToServer.prototype.KEY_OS_NAME = 'os-name'; | |
| 86 remoting.LogToServer.prototype.VALUE_OS_NAME_WINDOWS = 'Windows'; | |
| 87 remoting.LogToServer.prototype.VALUE_OS_NAME_LINUX = 'Linux'; | |
| 88 remoting.LogToServer.prototype.VALUE_OS_NAME_MAC = 'Mac'; | |
| 89 remoting.LogToServer.prototype.VALUE_OS_NAME_CHROMEOS = 'ChromeOS'; | |
|
Jamie
2011/11/10 23:27:43
What about other OSes? Seems like we should have a
simonmorris
2011/11/11 17:11:23
The logging stanza, and the logging backend, are h
| |
| 90 | |
| 91 remoting.LogToServer.prototype.KEY_OS_VERSION = 'os-version'; | |
| 92 | |
| 93 remoting.LogToServer.prototype.KEY_CPU = 'cpu'; | |
| 94 | |
| 95 remoting.LogToServer.prototype.KEY_BROWSER_VERSION = 'browser-version'; | |
| 96 | |
| 97 remoting.LogToServer.prototype.KEY_WEBAPP_VERSION = 'webapp-version'; | |
| 98 | |
| 99 /** | |
| 100 * Logs a client session state change. | |
| 101 * | |
| 102 * @param {remoting.ClientSession.State} state | |
| 103 * @param {remoting.ClientSession.ConnectionError} connectionError | |
| 104 */ | |
| 105 remoting.LogToServer.prototype.logClientSessionStateChange = | |
| 106 function(state, connectionError) { | |
| 107 var entry = this.makeClientSessionStateChange(state, connectionError); | |
| 108 this.addHostFields(entry); | |
|
Jamie
2011/11/10 23:27:43
This feels a little awkward. Would it be much more
simonmorris
2011/11/11 17:11:23
Done.
| |
| 109 this.addChromeVersionField(entry); | |
| 110 this.addWebappVersionField(entry); | |
| 111 this.log(entry); | |
| 112 }; | |
| 113 | |
| 114 /** | |
| 115 * Sends a log entry to the server. | |
| 116 * | |
| 117 * @private | |
| 118 * @param {Object.<string, string>} entry | |
| 119 */ | |
| 120 remoting.LogToServer.prototype.log = function(entry) { | |
| 121 // Convert the entry to a string. | |
| 122 var entryStr = '<gr:entry '; | |
| 123 for (var key in entry) { | |
| 124 entryStr += key + '="' + entry[key] + '" '; | |
|
Jamie
2011/11/10 23:27:43
I feel we ought to be XML-escaping these. I don't
simonmorris
2011/11/11 17:11:23
Done.
| |
| 125 } | |
| 126 entryStr += '/>'; | |
| 127 // Store that string. | |
| 128 this.pendingEntries.push(entryStr); | |
| 129 // Stop if there's no connection to the server. | |
| 130 if (!remoting.wcs) { | |
| 131 return; | |
| 132 } | |
| 133 // Send all pending entries to the server. | |
| 134 var stanza = '<cli:iq to="remoting@bot.talk.google.com" type="set" ' + | |
| 135 'xmlns:cli="jabber:client"><gr:log xmlns:gr="google:remoting">'; | |
| 136 while (this.pendingEntries.length > 0) { | |
| 137 stanza += /** @type string */ this.pendingEntries.shift(); | |
| 138 } | |
| 139 stanza += '</gr:log></cli:iq>'; | |
| 140 remoting.debug.log('Sending log iq: ' + stanza); | |
|
Jamie
2011/11/10 23:27:43
Is it worth using Gary's pretty-printing code to l
simonmorris
2011/11/11 17:11:23
Done, though the prett-printing code doesn't do an
| |
| 141 remoting.wcs.sendIq(stanza); | |
|
Wez
2011/11/11 01:41:04
If we're unable to send the stanza then we'll lose
simonmorris
2011/11/11 17:11:23
WCS sends stanzas asynchronously, so we don't know
| |
| 142 }; | |
| 143 | |
| 144 /** | |
| 145 * Makes a log entry with basic fields. | |
| 146 * | |
| 147 * @private | |
| 148 * @return {Object.<string, string>} | |
| 149 */ | |
| 150 remoting.LogToServer.prototype.makeBasicEntry = function() { | |
|
Jamie
2011/11/10 23:27:43
This could be a ctor for a LogEntry class.
simonmorris
2011/11/11 17:11:23
Done.
| |
| 151 /** @type Object.<string, string> */ var entry = {}; | |
| 152 entry[this.KEY_ROLE] = this.VALUE_ROLE_CLIENT; | |
| 153 return entry; | |
| 154 }; | |
| 155 | |
| 156 /** | |
| 157 * Makes a log entry for a change of client session state. | |
| 158 * | |
| 159 * @private | |
| 160 * @param {remoting.ClientSession.State} state | |
| 161 * @param {remoting.ClientSession.ConnectionError} connectionError | |
| 162 * @return {Object.<string, string>} | |
| 163 */ | |
| 164 remoting.LogToServer.prototype.makeClientSessionStateChange = | |
| 165 function(state, connectionError) { | |
| 166 var entry = this.makeBasicEntry(); | |
| 167 entry[this.KEY_EVENT_NAME] = this.VALUE_EVENT_NAME_SESSION_STATE; | |
| 168 entry[this.KEY_SESSION_STATE] = this.VALUE_SESSION_STATE(state); | |
| 169 if (connectionError != remoting.ClientSession.ConnectionError.NONE) { | |
| 170 entry[this.KEY_CONNECTION_ERROR] = | |
| 171 this.VALUE_CONNECTION_ERROR(connectionError); | |
| 172 } | |
| 173 return entry; | |
| 174 }; | |
| 175 | |
| 176 /** | |
| 177 * Adds fields describing the host to a given log entry. | |
| 178 * | |
| 179 * @private | |
| 180 * @param {Object.<string, string>} entry | |
| 181 */ | |
| 182 remoting.LogToServer.prototype.addHostFields = function(entry) { | |
| 183 var host = this.getHostData(); | |
| 184 if (host) { | |
| 185 if (host.os_name.length > 0) { | |
| 186 entry[this.KEY_OS_NAME] = host.os_name; | |
| 187 } | |
| 188 if (host.os_version.length > 0) { | |
| 189 entry[this.KEY_OS_VERSION] = host.os_version; | |
| 190 } | |
| 191 if (host.cpu.length > 0) { | |
| 192 entry[this.KEY_CPU] = host.cpu; | |
| 193 } | |
| 194 } | |
| 195 }; | |
| 196 | |
| 197 /** | |
| 198 * Extracts host data from the userAgent string. | |
| 199 * | |
| 200 * @private | |
| 201 * @return {{os_name:string, os_version:string, cpu:string} | null} | |
| 202 */ | |
| 203 remoting.LogToServer.prototype.getHostData = function() { | |
|
Jamie
2011/11/10 23:27:43
This doesn't need to be a member function. Better
simonmorris
2011/11/11 17:11:23
I don't understand why that would be better. Maybe
Jamie
2011/11/11 19:08:40
I just meant that you don't need a LogToServer obj
| |
| 204 return this.extractHostDataFrom(navigator.userAgent); | |
| 205 }; | |
| 206 | |
| 207 /** | |
| 208 * Extracts host data from the given userAgent string. | |
| 209 * | |
| 210 * @private | |
| 211 * @param {string} s | |
| 212 * @return {{os_name:string, os_version:string, cpu:string} | null} | |
| 213 */ | |
| 214 remoting.LogToServer.prototype.extractHostDataFrom = function(s) { | |
|
Jamie
2011/11/10 23:27:43
Ditto. In fact, unless it's called with anything o
simonmorris
2011/11/11 17:11:23
When I tested the code, it was called with the str
Jamie
2011/11/11 19:08:40
Fair point, thanks for clarifying.
| |
| 215 // Sample userAgent strings: | |
| 216 // 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 ' + | |
| 217 // '(KHTML, like Gecko) Chrome/15.0.874.106 Safari/535.2' | |
| 218 // 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.8 ' + | |
| 219 // '(KHTML, like Gecko) Chrome/17.0.933.0 Safari/535.8' | |
| 220 // 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 ' + | |
| 221 // '(KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1' | |
| 222 // 'Mozilla/5.0 (X11; CrOS i686 14.811.154) AppleWebKit/535.1 ' + | |
| 223 // '(KHTML, like Gecko) Chrome/14.0.835.204 Safari/535.1' | |
| 224 var match = new RegExp('Windows NT ([0-9\\.]*)').exec(s); | |
| 225 if (match && (match.length >= 2)) { | |
| 226 return { | |
| 227 'os_name': this.VALUE_OS_NAME_WINDOWS, | |
| 228 'os_version': match[1], | |
| 229 'cpu': '' | |
| 230 }; | |
| 231 } | |
| 232 match = new RegExp('Linux ([a-zA-Z0-9_]*)').exec(s); | |
| 233 if (match && (match.length >= 2)) { | |
| 234 return { | |
| 235 'os_name': this.VALUE_OS_NAME_LINUX, | |
| 236 'os_version' : '', | |
| 237 'cpu': match[1] | |
| 238 }; | |
| 239 } | |
| 240 match = new RegExp('([a-zA-Z]*) Mac OS X ([0-9_]*)').exec(s); | |
| 241 if (match && (match.length >= 3)) { | |
| 242 return { | |
| 243 'os_name': this.VALUE_OS_NAME_MAC, | |
| 244 'os_version': match[2].replace(/_/g, '.'), | |
| 245 'cpu': match[1] | |
| 246 }; | |
| 247 } | |
| 248 match = new RegExp('CrOS ([a-zA-Z0-9]*) ([0-9.]*)').exec(s); | |
| 249 if (match && (match.length >= 3)) { | |
| 250 return { | |
| 251 'os_name': this.VALUE_OS_NAME_CHROMEOS, | |
| 252 'os_version': match[2], | |
| 253 'cpu': match[1] | |
| 254 }; | |
| 255 } | |
| 256 return null; | |
| 257 }; | |
| 258 | |
| 259 /** | |
| 260 * Adds a field specifying the browser version to a given log entry. | |
| 261 * | |
| 262 * @private | |
| 263 * @param {Object.<string, string>} entry | |
| 264 */ | |
| 265 remoting.LogToServer.prototype.addChromeVersionField = function(entry) { | |
| 266 var version = this.getChromeVersion(); | |
| 267 if (version != null) { | |
| 268 entry[this.KEY_BROWSER_VERSION] = version; | |
| 269 } | |
| 270 }; | |
| 271 | |
| 272 /** | |
| 273 * Extracts the Chrome version from the userAgent string. | |
| 274 * | |
| 275 * @private | |
| 276 * @return {string | null} | |
| 277 */ | |
| 278 remoting.LogToServer.prototype.getChromeVersion = function() { | |
|
Jamie
2011/11/10 23:27:43
My comments for getHostData also apply to these fu
simonmorris
2011/11/11 17:11:23
Mine too.
| |
| 279 return this.extractChromeVersionFrom(navigator.userAgent); | |
| 280 }; | |
| 281 | |
| 282 /** | |
| 283 * Extracts the Chrome version from the given userAgent string. | |
| 284 * | |
| 285 * @private | |
| 286 * @param {string} s | |
| 287 * @return {string | null} | |
| 288 */ | |
| 289 remoting.LogToServer.prototype.extractChromeVersionFrom = function(s) { | |
| 290 var match = new RegExp('Chrome/([0-9.]*)').exec(s); | |
| 291 if (match && (match.length >= 2)) { | |
| 292 return match[1]; | |
| 293 } | |
| 294 return null; | |
| 295 }; | |
| 296 | |
| 297 /** | |
| 298 * Adds a field specifying the webapp version to a given log entry. | |
| 299 * | |
| 300 * @private | |
| 301 * @param {Object.<string, string>} entry | |
| 302 */ | |
| 303 remoting.LogToServer.prototype.addWebappVersionField = function(entry) { | |
| 304 entry[this.KEY_WEBAPP_VERSION] = chrome.app.getDetails().version; | |
| 305 }; | |
| OLD | NEW |