Log signal strategy progress to server.
[chromium-blink-merge.git] / remoting / webapp / crd / js / server_log_entry.js
blob0205ea245d8f2e76e5fee2d0716f86088f1db146
1 // Copyright (c) 2012 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.
5 /**
6  * @fileoverview
7  * A class of server log entries.
8  *
9  * Any changes to the values here need to be coordinated with the host and
10  * server/log proto code.
11  * See remoting/signaling/server_log_entry.{cc|h}
12  */
14 'use strict';
16 /** @suppress {duplicate} */
17 var remoting = remoting || {};
19 /**
20  * @private
21  * @constructor
22  */
23 remoting.ServerLogEntry = function() {
24   /** @type Object.<string, string> */ this.dict = {};
27 /** @private */
28 remoting.ServerLogEntry.KEY_EVENT_NAME_ = 'event-name';
29 /** @private */
30 remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_STATE_ = 'session-state';
31 /** @private */
32 remoting.ServerLogEntry.VALUE_EVENT_NAME_SIGNAL_STRATEGY_PROGRESS_ =
33     'signal-strategy-progress';
34 /** @private */
35 remoting.ServerLogEntry.KEY_SESSION_ID_ = 'session-id';
36 /** @private */
37 remoting.ServerLogEntry.KEY_ROLE_ = 'role';
38 /** @private */
39 remoting.ServerLogEntry.VALUE_ROLE_CLIENT_ = 'client';
40 /** @private */
41 remoting.ServerLogEntry.KEY_SESSION_STATE_ = 'session-state';
42 /** @private */
43 remoting.ServerLogEntry.KEY_CONNECTION_TYPE_ = 'connection-type';
44 /** @private */
45 remoting.ServerLogEntry.KEY_SIGNAL_STRATEGY_TYPE_ = 'signal-strategy-type';
46 /** @private */
47 remoting.ServerLogEntry.KEY_SIGNAL_STRATEGY_PROGRESS_ =
48     'signal-strategy-progress';
49 /** @private */
50 remoting.ServerLogEntry.KEY_SIGNAL_STRATEGY_ELAPSED_TIME_ =
51     'signal-strategy-elapsed-time';
53 /**
54  * @private
55  * @param {remoting.ClientSession.State} state
56  * @return {string}
57  */
58 remoting.ServerLogEntry.getValueForSessionState_ = function(state) {
59   switch(state) {
60     case remoting.ClientSession.State.UNKNOWN:
61       return 'unknown';
62     case remoting.ClientSession.State.CREATED:
63       return 'created';
64     case remoting.ClientSession.State.CONNECTING:
65       return 'connecting';
66     case remoting.ClientSession.State.INITIALIZING:
67       return 'initializing';
68     case remoting.ClientSession.State.CONNECTED:
69       return 'connected';
70     case remoting.ClientSession.State.CLOSED:
71       return 'closed';
72     case remoting.ClientSession.State.FAILED:
73       return 'connection-failed';
74     case remoting.ClientSession.State.CONNECTION_DROPPED:
75       return 'connection-dropped';
76     case remoting.ClientSession.State.CONNECTION_CANCELED:
77       return 'connection-canceled';
78     default:
79       return 'undefined-' + state;
80   }
83 /** @private */
84 remoting.ServerLogEntry.KEY_CONNECTION_ERROR_ = 'connection-error';
86 /**
87  * @private
88  * @param {remoting.Error} connectionError
89  * @return {string}
90  */
91 remoting.ServerLogEntry.getValueForError_ = function(connectionError) {
92   // Directory service should be updated if a new string is added here as
93   // otherwise the error code will be ignored (i.e. recorded as 0 instead).
94   switch(connectionError) {
95     case remoting.Error.NONE:
96       return 'none';
97     case remoting.Error.INVALID_ACCESS_CODE:
98       return 'invalid-access-code';
99     case remoting.Error.MISSING_PLUGIN:
100       return 'missing_plugin';
101     case remoting.Error.AUTHENTICATION_FAILED:
102       return 'authentication-failed';
103     case remoting.Error.HOST_IS_OFFLINE:
104       return 'host-is-offline';
105     case remoting.Error.INCOMPATIBLE_PROTOCOL:
106       return 'incompatible-protocol';
107     case remoting.Error.BAD_PLUGIN_VERSION:
108       return 'bad-plugin-version';
109     case remoting.Error.NETWORK_FAILURE:
110       return 'network-failure';
111     case remoting.Error.HOST_OVERLOAD:
112       return 'host-overload';
113     case remoting.Error.P2P_FAILURE:
114       return 'p2p-failure';
115     case remoting.Error.UNEXPECTED:
116       return 'unexpected';
117     default:
118       return 'unknown-' + connectionError;
119   }
122 /** @private */
123 remoting.ServerLogEntry.KEY_SESSION_DURATION_ = 'session-duration';
125 /** @private */
126 remoting.ServerLogEntry.VALUE_EVENT_NAME_CONNECTION_STATISTICS_ =
127     "connection-statistics";
128 /** @private */
129 remoting.ServerLogEntry.KEY_VIDEO_BANDWIDTH_ = "video-bandwidth";
130 /** @private */
131 remoting.ServerLogEntry.KEY_CAPTURE_LATENCY_ = "capture-latency";
132 /** @private */
133 remoting.ServerLogEntry.KEY_ENCODE_LATENCY_ = "encode-latency";
134 /** @private */
135 remoting.ServerLogEntry.KEY_DECODE_LATENCY_ = "decode-latency";
136 /** @private */
137 remoting.ServerLogEntry.KEY_RENDER_LATENCY_ = "render-latency";
138 /** @private */
139 remoting.ServerLogEntry.KEY_ROUNDTRIP_LATENCY_ = "roundtrip-latency";
141 /** @private */
142 remoting.ServerLogEntry.KEY_OS_NAME_ = 'os-name';
143 /** @private */
144 remoting.ServerLogEntry.VALUE_OS_NAME_WINDOWS_ = 'Windows';
145 /** @private */
146 remoting.ServerLogEntry.VALUE_OS_NAME_LINUX_ = 'Linux';
147 /** @private */
148 remoting.ServerLogEntry.VALUE_OS_NAME_MAC_ = 'Mac';
149 /** @private */
150 remoting.ServerLogEntry.VALUE_OS_NAME_CHROMEOS_ = 'ChromeOS';
152 /** @private */
153 remoting.ServerLogEntry.KEY_OS_VERSION_ = 'os-version';
155 /** @private */
156 remoting.ServerLogEntry.KEY_CPU_ = 'cpu';
158 /** @private */
159 remoting.ServerLogEntry.KEY_BROWSER_VERSION_ = 'browser-version';
161 /** @private */
162 remoting.ServerLogEntry.KEY_WEBAPP_VERSION_ = 'webapp-version';
164 /** @private */
165 remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_ID_OLD_ = 'session-id-old';
167 /** @private */
168 remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_ID_NEW_ = 'session-id-new';
170 /** @private */
171 remoting.ServerLogEntry.KEY_MODE_ = 'mode';
172 /** @private */
173 remoting.ServerLogEntry.VALUE_MODE_IT2ME_ = 'it2me';
174 /** @private */
175 remoting.ServerLogEntry.VALUE_MODE_ME2ME_ = 'me2me';
176 /** @private */
177 remoting.ServerLogEntry.VALUE_MODE_APP_REMOTING_ = 'lgapp';
178 /** @private */
179 remoting.ServerLogEntry.VALUE_MODE_UNKNOWN_ = 'unknown';
182  * Sets one field in this log entry.
184  * @private
185  * @param {string} key
186  * @param {string} value
187  */
188 remoting.ServerLogEntry.prototype.set_ = function(key, value) {
189   this.dict[key] = value;
193  * Converts this object into an XML stanza.
195  * @return {string}
196  */
197 remoting.ServerLogEntry.prototype.toStanza = function() {
198   var stanza = '<gr:entry ';
199   for (var key in this.dict) {
200     stanza += escape(key) + '="' + escape(this.dict[key]) + '" ';
201   }
202   stanza += '/>';
203   return stanza;
207  * Prints this object on the debug log.
209  * @param {number} indentLevel the indentation level
210  */
211 remoting.ServerLogEntry.prototype.toDebugLog = function(indentLevel) {
212   /** @type Array.<string> */ var fields = [];
213   for (var key in this.dict) {
214     fields.push(key + ': ' + this.dict[key]);
215   }
216   console.log(Array(indentLevel+1).join("  ") + fields.join(', '));
220  * Makes a log entry for a change of client session state.
222  * @param {remoting.ClientSession.State} state
223  * @param {remoting.Error} connectionError
224  * @param {remoting.ClientSession.Mode} mode
225  * @return {remoting.ServerLogEntry}
226  */
227 remoting.ServerLogEntry.makeClientSessionStateChange = function(state,
228     connectionError, mode) {
229   var entry = new remoting.ServerLogEntry();
230   entry.set_(remoting.ServerLogEntry.KEY_ROLE_,
231              remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
232   entry.set_(remoting.ServerLogEntry.KEY_EVENT_NAME_,
233              remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_STATE_);
234   entry.set_(remoting.ServerLogEntry.KEY_SESSION_STATE_,
235              remoting.ServerLogEntry.getValueForSessionState_(state));
236   if (connectionError != remoting.Error.NONE) {
237     entry.set_(remoting.ServerLogEntry.KEY_CONNECTION_ERROR_,
238                remoting.ServerLogEntry.getValueForError_(connectionError));
239   }
240   entry.addModeField(mode);
241   return entry;
245  * Adds a session duration to a log entry.
247  * @param {number} sessionDuration
248  */
249 remoting.ServerLogEntry.prototype.addSessionDurationField = function(
250     sessionDuration) {
251   this.set_(remoting.ServerLogEntry.KEY_SESSION_DURATION_,
252             sessionDuration.toString());
256  * Makes a log entry for a set of connection statistics.
257  * Returns null if all the statistics were zero.
259  * @param {remoting.StatsAccumulator} statsAccumulator
260  * @param {string} connectionType
261  * @param {remoting.ClientSession.Mode} mode
262  * @return {?remoting.ServerLogEntry}
263  */
264 remoting.ServerLogEntry.makeStats = function(statsAccumulator,
265                                              connectionType,
266                                              mode) {
267   var entry = new remoting.ServerLogEntry();
268   entry.set_(remoting.ServerLogEntry.KEY_ROLE_,
269              remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
270   entry.set_(remoting.ServerLogEntry.KEY_EVENT_NAME_,
271              remoting.ServerLogEntry.VALUE_EVENT_NAME_CONNECTION_STATISTICS_);
272   if (connectionType) {
273     entry.set_(remoting.ServerLogEntry.KEY_CONNECTION_TYPE_,
274                connectionType);
275   }
276   entry.addModeField(mode);
277   var nonZero = false;
278   nonZero |= entry.addStatsField_(
279       remoting.ServerLogEntry.KEY_VIDEO_BANDWIDTH_,
280       remoting.ClientSession.STATS_KEY_VIDEO_BANDWIDTH, statsAccumulator);
281   nonZero |= entry.addStatsField_(
282       remoting.ServerLogEntry.KEY_CAPTURE_LATENCY_,
283       remoting.ClientSession.STATS_KEY_CAPTURE_LATENCY, statsAccumulator);
284   nonZero |= entry.addStatsField_(
285       remoting.ServerLogEntry.KEY_ENCODE_LATENCY_,
286       remoting.ClientSession.STATS_KEY_ENCODE_LATENCY, statsAccumulator);
287   nonZero |= entry.addStatsField_(
288       remoting.ServerLogEntry.KEY_DECODE_LATENCY_,
289       remoting.ClientSession.STATS_KEY_DECODE_LATENCY, statsAccumulator);
290   nonZero |= entry.addStatsField_(
291       remoting.ServerLogEntry.KEY_RENDER_LATENCY_,
292       remoting.ClientSession.STATS_KEY_RENDER_LATENCY, statsAccumulator);
293   nonZero |= entry.addStatsField_(
294       remoting.ServerLogEntry.KEY_ROUNDTRIP_LATENCY_,
295       remoting.ClientSession.STATS_KEY_ROUNDTRIP_LATENCY, statsAccumulator);
296   if (nonZero) {
297     return entry;
298   }
299   return null;
303  * Adds one connection statistic to a log entry.
305  * @private
306  * @param {string} entryKey
307  * @param {string} statsKey
308  * @param {remoting.StatsAccumulator} statsAccumulator
309  * @return {boolean} whether the statistic is non-zero
310  */
311 remoting.ServerLogEntry.prototype.addStatsField_ = function(
312     entryKey, statsKey, statsAccumulator) {
313   var val = statsAccumulator.calcMean(statsKey);
314   this.set_(entryKey, val.toFixed(2));
315   return (val != 0);
319  * Makes a log entry for a "this session ID is old" event.
321  * @param {string} sessionId
322  * @param {remoting.ClientSession.Mode} mode
323  * @return {remoting.ServerLogEntry}
324  */
325 remoting.ServerLogEntry.makeSessionIdOld = function(sessionId, mode) {
326   var entry = new remoting.ServerLogEntry();
327   entry.set_(remoting.ServerLogEntry.KEY_ROLE_,
328              remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
329   entry.set_(remoting.ServerLogEntry.KEY_EVENT_NAME_,
330              remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_ID_OLD_);
331   entry.addSessionIdField(sessionId);
332   entry.addModeField(mode);
333   return entry;
337  * Makes a log entry for a "this session ID is new" event.
339  * @param {string} sessionId
340  * @param {remoting.ClientSession.Mode} mode
341  * @return {remoting.ServerLogEntry}
342  */
343 remoting.ServerLogEntry.makeSessionIdNew = function(sessionId, mode) {
344   var entry = new remoting.ServerLogEntry();
345   entry.set_(remoting.ServerLogEntry.KEY_ROLE_,
346              remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
347   entry.set_(remoting.ServerLogEntry.KEY_EVENT_NAME_,
348              remoting.ServerLogEntry.VALUE_EVENT_NAME_SESSION_ID_NEW_);
349   entry.addSessionIdField(sessionId);
350   entry.addModeField(mode);
351   return entry;
355  * Makes a log entry for a "signal strategy fallback" event.
357  * @param {string} sessionId
358  * @param {remoting.SignalStrategy.Type} strategyType
359  * @param {remoting.FallbackSignalStrategy.Progress} progress
360  * @param {number} elapsedTimeInMs
361  * @return {remoting.ServerLogEntry}
362  */
363 remoting.ServerLogEntry.makeSignalStrategyProgress =
364     function(sessionId, strategyType, progress, elapsedTimeInMs) {
365   var entry = new remoting.ServerLogEntry();
366   entry.set_(remoting.ServerLogEntry.KEY_ROLE_,
367              remoting.ServerLogEntry.VALUE_ROLE_CLIENT_);
368   entry.set_(
369       remoting.ServerLogEntry.KEY_EVENT_NAME_,
370       remoting.ServerLogEntry.VALUE_EVENT_NAME_SIGNAL_STRATEGY_PROGRESS_);
371   entry.addSessionIdField(sessionId);
372   entry.set_(remoting.ServerLogEntry.KEY_SIGNAL_STRATEGY_TYPE_, strategyType);
373   entry.set_(remoting.ServerLogEntry.KEY_SIGNAL_STRATEGY_PROGRESS_, progress);
374   entry.set_(remoting.ServerLogEntry.KEY_SIGNAL_STRATEGY_ELAPSED_TIME_,
375              String(elapsedTimeInMs));
377   return entry;
381  * Adds a session ID field to this log entry.
383  * @param {string} sessionId
384  */
385 remoting.ServerLogEntry.prototype.addSessionIdField = function(sessionId) {
386   this.set_(remoting.ServerLogEntry.KEY_SESSION_ID_, sessionId);
390  * Adds fields describing the host to this log entry.
391  */
392 remoting.ServerLogEntry.prototype.addHostFields = function() {
393   var host = remoting.ServerLogEntry.getHostData_();
394   if (host) {
395     if (host.os_name.length > 0) {
396       this.set_(remoting.ServerLogEntry.KEY_OS_NAME_, host.os_name);
397     }
398     if (host.os_version.length > 0) {
399       this.set_(remoting.ServerLogEntry.KEY_OS_VERSION_, host.os_version);
400     }
401     if (host.cpu.length > 0) {
402       this.set_(remoting.ServerLogEntry.KEY_CPU_, host.cpu);
403     }
404   }
408  * Extracts host data from the userAgent string.
410  * @private
411  * @return {{os_name:string, os_version:string, cpu:string} | null}
412  */
413 remoting.ServerLogEntry.getHostData_ = function() {
414   return remoting.ServerLogEntry.extractHostDataFrom_(navigator.userAgent);
418  * Extracts host data from the given userAgent string.
420  * @private
421  * @param {string} s
422  * @return {{os_name:string, os_version:string, cpu:string} | null}
423  */
424 remoting.ServerLogEntry.extractHostDataFrom_ = function(s) {
425   // Sample userAgent strings:
426   // 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 ' +
427   //   '(KHTML, like Gecko) Chrome/15.0.874.106 Safari/535.2'
428   // 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.8 ' +
429   //   '(KHTML, like Gecko) Chrome/17.0.933.0 Safari/535.8'
430   // 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 ' +
431   //   '(KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1'
432   // 'Mozilla/5.0 (X11; CrOS i686 14.811.154) AppleWebKit/535.1 ' +
433   //   '(KHTML, like Gecko) Chrome/14.0.835.204 Safari/535.1'
434   var match = new RegExp('Windows NT ([0-9\\.]*)').exec(s);
435   if (match && (match.length >= 2)) {
436     return {
437         'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_WINDOWS_,
438         'os_version': match[1],
439         'cpu': ''
440     };
441   }
442   match = new RegExp('Linux ([a-zA-Z0-9_]*)').exec(s);
443   if (match && (match.length >= 2)) {
444     return {
445         'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_LINUX_,
446         'os_version' : '',
447         'cpu': match[1]
448     };
449   }
450   match = new RegExp('([a-zA-Z]*) Mac OS X ([0-9_]*)').exec(s);
451   if (match && (match.length >= 3)) {
452     return {
453         'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_MAC_,
454         'os_version': match[2].replace(/_/g, '.'),
455         'cpu': match[1]
456     };
457   }
458   match = new RegExp('CrOS ([a-zA-Z0-9]*) ([0-9.]*)').exec(s);
459   if (match && (match.length >= 3)) {
460     return {
461         'os_name': remoting.ServerLogEntry.VALUE_OS_NAME_CHROMEOS_,
462         'os_version': match[2],
463         'cpu': match[1]
464     };
465   }
466   return null;
470  * Adds a field specifying the browser version to this log entry.
471  */
472 remoting.ServerLogEntry.prototype.addChromeVersionField = function() {
473   var version = remoting.getChromeVersion();
474   if (version != null) {
475     this.set_(remoting.ServerLogEntry.KEY_BROWSER_VERSION_, version);
476   }
480  * Adds a field specifying the webapp version to this log entry.
481  */
482 remoting.ServerLogEntry.prototype.addWebappVersionField = function() {
483   var manifest = chrome.runtime.getManifest();
484   if (manifest && manifest.version) {
485     this.set_(remoting.ServerLogEntry.KEY_WEBAPP_VERSION_, manifest.version);
486   }
490  * Adds a field specifying the mode to this log entry.
492  * @param {remoting.ClientSession.Mode} mode
493  */
494 remoting.ServerLogEntry.prototype.addModeField = function(mode) {
495   this.set_(remoting.ServerLogEntry.KEY_MODE_,
496             remoting.ServerLogEntry.getModeField_(mode));
500  * Gets the value of the mode field to be put in a log entry.
502  * @private
503  * @param {remoting.ClientSession.Mode} mode
504  * @return {string}
505  */
506 remoting.ServerLogEntry.getModeField_ = function(mode) {
507   switch(mode) {
508     case remoting.ClientSession.Mode.IT2ME:
509       return remoting.ServerLogEntry.VALUE_MODE_IT2ME_;
510     case remoting.ClientSession.Mode.ME2ME:
511       return remoting.ServerLogEntry.VALUE_MODE_ME2ME_;
512     case remoting.ClientSession.Mode.APP_REMOTING:
513       return remoting.ServerLogEntry.VALUE_MODE_APP_REMOTING_;
514     default:
515       return remoting.ServerLogEntry.VALUE_MODE_UNKNOWN_;
516   }