Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / webapp / base / js / session_logger.js
blob40a594c90e5660a0b0af7338536a01a65e57fcfd
1 // Copyright 2015 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.
6 /** @suppress {duplicate} */
7 var remoting = remoting || {};
9 (function() {
11 'use strict';
13 /**
14  * This class defines a remoting.Logger implementation that generates
15  * log entries in the format of remoting.ChromotingEvent.
16  *
17  * @param {remoting.ChromotingEvent.Role} role
18  * @param {function(!Object)} writeLogEntry
19  *
20  * @constructor
21  * @implements {remoting.Logger}
22  */
23 remoting.SessionLogger = function(role, writeLogEntry) {
24   /** @private */
25   this.role_ = role;
26   /** @private */
27   this.writeLogEntry_ = writeLogEntry;
28   /** @private */
29   this.statsAccumulator_ = new remoting.StatsAccumulator();
30   /** @private */
31   this.sessionId_ = '';
32   /** @private */
33   this.sessionIdGenerationTime_ = 0;
34   /** @private */
35   this.sessionStartTime_ = new Date().getTime();
36   /** @private {remoting.ChromotingEvent.ConnectionType} */
37   this.connectionType_;
38   /** @private {remoting.ChromotingEvent.SessionEntryPoint} */
39   this.entryPoint_;
40   /** @private {remoting.ChromotingEvent.SessionState} */
41   this.previousSessionState_;
42   /** @private */
43   this.authTotalTime_ = 0;
44   /** @private */
45   this.hostVersion_ = '';
47   /** @private */
48   this.mode_ = remoting.ChromotingEvent.Mode.ME2ME;
50   this.setSessionId_();
53 /**
54  * @param {remoting.ChromotingEvent.SessionEntryPoint} entryPoint
55  */
56 remoting.SessionLogger.prototype.setEntryPoint = function(entryPoint) {
57   this.entryPoint_ = entryPoint;
60 /** @override {remoting.Logger} */
61 remoting.SessionLogger.prototype.setAuthTotalTime = function(totalTime) {
62   this.authTotalTime_ = totalTime;
65 /** @override {remoting.Logger} */
66 remoting.SessionLogger.prototype.setHostVersion = function(hostVersion) {
67   this.hostVersion_ = hostVersion;
70 /** @override {remoting.Logger} */
71 remoting.SessionLogger.prototype.setConnectionType = function(connectionType) {
72   this.connectionType_ = toConnectionType(connectionType);
75 /** @override {remoting.Logger} */
76 remoting.SessionLogger.prototype.setLogEntryMode = function(mode) {
77   this.mode_ = mode;
80 /** @override {remoting.Logger} */
81 remoting.SessionLogger.prototype.getSessionId = function() {
82   return this.sessionId_;
85 /** @override {remoting.Logger} */
86 remoting.SessionLogger.prototype.logSignalStrategyProgress =
87     function(strategyType, progress) {
88   this.maybeExpireSessionId_();
89   var entry = new remoting.ChromotingEvent(
90       remoting.ChromotingEvent.Type.SIGNAL_STRATEGY_PROGRESS);
91   entry.signal_strategy_type = toSignalStrategyType(strategyType);
92   entry.signal_strategy_progress = toSignalStrategyProgress(progress);
94   this.fillEvent_(entry);
95   this.log_(entry);
98 /** @override {remoting.Logger} */
99 remoting.SessionLogger.prototype.logClientSessionStateChange = function(
100     state, connectionError, xmppError) {
101   this.logSessionStateChange(
102       toSessionState(state),
103       toConnectionError(connectionError),
104       xmppError);
108  * @param {remoting.ChromotingEvent.SessionState} state
109  * @param {remoting.ChromotingEvent.ConnectionError} error
110  * @param {remoting.ChromotingEvent.XmppError=} opt_XmppError
111  */
112 remoting.SessionLogger.prototype.logSessionStateChange = function(
113     state, error, opt_XmppError) {
114   this.maybeExpireSessionId_();
116   var entry = this.makeSessionStateChange_(
117       state, error,
118       /** @type {?remoting.ChromotingEvent.XmppError} */ (opt_XmppError));
119   entry.previous_session_state = this.previousSessionState_;
120   this.previousSessionState_ = state;
122   this.log_(entry);
124   // Don't accumulate connection statistics across state changes.
125   this.logAccumulatedStatistics_();
126   this.statsAccumulator_.empty();
129 /** @override {remoting.Logger} */
130 remoting.SessionLogger.prototype.logStatistics = function(stats) {
131   this.maybeExpireSessionId_();
132   // Store the statistics.
133   this.statsAccumulator_.add(stats);
134   // Send statistics to the server if they've been accumulating for at least
135   // 60 seconds.
136   if (this.statsAccumulator_.getTimeSinceFirstValue() >=
137       remoting.Logger.CONNECTION_STATS_ACCUMULATE_TIME) {
138     this.logAccumulatedStatistics_();
139   }
143  * @param {remoting.ChromotingEvent.SessionState} state
144  * @param {remoting.ChromotingEvent.ConnectionError} error
145  * @param {?remoting.ChromotingEvent.XmppError} xmppError
146  * @return {remoting.ChromotingEvent}
147  * @private
148  */
149 remoting.SessionLogger.prototype.makeSessionStateChange_ =
150     function(state, error, xmppError) {
151   var entry = new remoting.ChromotingEvent(
152       remoting.ChromotingEvent.Type.SESSION_STATE);
153   entry.connection_error = error;
154   entry.session_state = state;
156   if (Boolean(xmppError)) {
157     entry.xmpp_error = xmppError;
158   }
160   this.fillEvent_(entry);
161   return entry;
165  * @return {remoting.ChromotingEvent}
166  * @private
167  */
168 remoting.SessionLogger.prototype.makeSessionIdNew_ = function() {
169   var entry = new remoting.ChromotingEvent(
170       remoting.ChromotingEvent.Type.SESSION_ID_NEW);
171   this.fillEvent_(entry);
172   return entry;
176  * @return {remoting.ChromotingEvent}
177  * @private
178  */
179 remoting.SessionLogger.prototype.makeSessionIdOld_ = function() {
180   var entry = new remoting.ChromotingEvent(
181       remoting.ChromotingEvent.Type.SESSION_ID_OLD);
182   this.fillEvent_(entry);
183   return entry;
187  * @return {remoting.ChromotingEvent}
188  * @private
189  */
190 remoting.SessionLogger.prototype.makeStats_ = function() {
191   var perfStats = this.statsAccumulator_.getPerfStats();
192   if (Boolean(perfStats)) {
193     var entry = new remoting.ChromotingEvent(
194         remoting.ChromotingEvent.Type.CONNECTION_STATISTICS);
195     this.fillEvent_(entry);
196     entry.video_bandwidth = perfStats.videoBandwidth;
197     entry.capture_latency = perfStats.captureLatency;
198     entry.encode_latency = perfStats.encodeLatency;
199     entry.decode_latency = perfStats.decodeLatency;
200     entry.render_latency = perfStats.renderLatency;
201     entry.roundtrip_latency = perfStats.roundtripLatency;
202     return entry;
203   }
204   return null;
208  * Moves connection statistics from the accumulator to the log server.
210  * If all the statistics are zero, then the accumulator is still emptied,
211  * but the statistics are not sent to the log server.
213  * @private
214  */
215 remoting.SessionLogger.prototype.logAccumulatedStatistics_ = function() {
216   var entry = this.makeStats_();
217   if (entry) {
218     this.log_(entry);
219   }
220   this.statsAccumulator_.empty();
224  * @param {remoting.ChromotingEvent} entry
225  * @private
226  */
227 remoting.SessionLogger.prototype.fillEvent_ = function(entry) {
228   entry.session_id = this.sessionId_;
229   entry.mode = this.mode_;
230   entry.role = this.role_;
231   entry.session_entry_point = this.entryPoint_;
232   var sessionDurationInSeconds =
233       (new Date().getTime() - this.sessionStartTime_ -
234           this.authTotalTime_) / 1000.0;
235   entry.session_duration = sessionDurationInSeconds;
236   if (Boolean(this.connectionType_)) {
237     entry.connection_type = this.connectionType_;
238   }
239   entry.host_version = this.hostVersion_;
243  * Sends a log entry to the server.
244  * @param {remoting.ChromotingEvent} entry
245  * @private
246  */
247 remoting.SessionLogger.prototype.log_ = function(entry) {
248   this.writeLogEntry_(/** @type {!Object} */ (base.deepCopy(entry)));
252  * Sets the session ID to a random string.
253  * @private
254  */
255 remoting.SessionLogger.prototype.setSessionId_ = function() {
256   var random = new Uint8Array(20);
257   window.crypto.getRandomValues(random);
258   this.sessionId_ = window.btoa(String.fromCharCode.apply(null, random));
259   this.sessionIdGenerationTime_ = new Date().getTime();
263  * Clears the session ID.
265  * @private
266  */
267 remoting.SessionLogger.prototype.clearSessionId_ = function() {
268   this.sessionId_ = '';
269   this.sessionIdGenerationTime_ = 0;
273  * Sets a new session ID, if the current session ID has reached its maximum age.
275  * This method also logs the old and new session IDs to the server, in separate
276  * log entries.
278  * @private
279  */
280 remoting.SessionLogger.prototype.maybeExpireSessionId_ = function() {
281   if ((this.sessionId_ !== '') &&
282       (new Date().getTime() - this.sessionIdGenerationTime_ >=
283       remoting.Logger.MAX_SESSION_ID_AGE)) {
284     // Log the old session ID.
285     var entry = this.makeSessionIdOld_();
286     this.log_(entry);
287     // Generate a new session ID.
288     this.setSessionId_();
289     // Log the new session ID.
290     entry = this.makeSessionIdNew_();
291     this.log_(entry);
292   }
295 /** @return {remoting.SessionLogger} */
296 remoting.SessionLogger.createForClient = function() {
297   return new remoting.SessionLogger(remoting.ChromotingEvent.Role.CLIENT,
298                                     remoting.TelemetryEventWriter.Client.write);
302  * TODO(kelvinp): Consolidate the two enums (crbug.com/504200)
303  * @param {remoting.ClientSession.State} state
304  * @return {remoting.ChromotingEvent.SessionState}
305  */
306 function toSessionState(state) {
307   var SessionState = remoting.ChromotingEvent.SessionState;
308   switch(state) {
309     case remoting.ClientSession.State.UNKNOWN:
310       return SessionState.UNKNOWN;
311     case remoting.ClientSession.State.INITIALIZING:
312       return SessionState.INITIALIZING;
313     case remoting.ClientSession.State.CONNECTING:
314       return SessionState.CONNECTING;
315     case remoting.ClientSession.State.AUTHENTICATED:
316       return SessionState.AUTHENTICATED;
317     case remoting.ClientSession.State.CONNECTED:
318       return SessionState.CONNECTED;
319     case remoting.ClientSession.State.CLOSED:
320       return SessionState.CLOSED;
321     case remoting.ClientSession.State.FAILED:
322       return SessionState.CONNECTION_FAILED;
323     case remoting.ClientSession.State.CONNECTION_DROPPED:
324       return SessionState.CONNECTION_DROPPED;
325     case remoting.ClientSession.State.CONNECTION_CANCELED:
326       return SessionState.CONNECTION_CANCELED;
327     default:
328       throw new Error('Unknown session state : ' + state);
329   }
333  * @param {remoting.Error} error
334  * @return {remoting.ChromotingEvent.ConnectionError}
335  */
336 function toConnectionError(error) {
337   var ConnectionError = remoting.ChromotingEvent.ConnectionError;
338   switch (error.getTag()) {
339     case remoting.Error.Tag.NONE:
340       return ConnectionError.NONE;
341     case remoting.Error.Tag.INVALID_ACCESS_CODE:
342       return ConnectionError.INVALID_ACCESS_CODE;
343     case remoting.Error.Tag.MISSING_PLUGIN:
344       return ConnectionError.MISSING_PLUGIN;
345     case remoting.Error.Tag.AUTHENTICATION_FAILED:
346       return ConnectionError.AUTHENTICATION_FAILED;
347     case remoting.Error.Tag.HOST_IS_OFFLINE:
348       return ConnectionError.HOST_OFFLINE;
349     case remoting.Error.Tag.INCOMPATIBLE_PROTOCOL:
350       return ConnectionError.INCOMPATIBLE_PROTOCOL;
351     case remoting.Error.Tag.BAD_PLUGIN_VERSION:
352       return ConnectionError.ERROR_BAD_PLUGIN_VERSION;
353     case remoting.Error.Tag.NETWORK_FAILURE:
354       return ConnectionError.NETWORK_FAILURE;
355     case remoting.Error.Tag.HOST_OVERLOAD:
356       return ConnectionError.HOST_OVERLOAD;
357     case remoting.Error.Tag.P2P_FAILURE:
358       return ConnectionError.P2P_FAILURE;
359     case remoting.Error.Tag.CLIENT_SUSPENDED:
360       return ConnectionError.CLIENT_SUSPENDED;
361     case remoting.Error.Tag.UNEXPECTED:
362       return ConnectionError.UNEXPECTED;
363     default:
364       throw new Error('Unknown error Tag : ' + error.getTag());
365   }
369  * @param {remoting.SignalStrategy.Type} type
370  * @return {remoting.ChromotingEvent.SignalStrategyType}
371  */
372 function toSignalStrategyType(type) {
373   switch (type) {
374     case remoting.SignalStrategy.Type.XMPP:
375       return remoting.ChromotingEvent.SignalStrategyType.XMPP;
376     case remoting.SignalStrategy.Type.WCS:
377       return remoting.ChromotingEvent.SignalStrategyType.WCS;
378     default:
379       throw new Error('Unknown signal strategy type : ' + type);
380   }
384  * @param {remoting.FallbackSignalStrategy.Progress} progress
385  * @return {remoting.ChromotingEvent.SignalStrategyProgress}
386  */
387 function toSignalStrategyProgress(progress) {
388   var Progress = remoting.FallbackSignalStrategy.Progress;
389   switch (progress) {
390     case Progress.SUCCEEDED:
391       return remoting.ChromotingEvent.SignalStrategyProgress.SUCCEEDED;
392     case Progress.FAILED:
393       return remoting.ChromotingEvent.SignalStrategyProgress.FAILED;
394     case Progress.TIMED_OUT:
395       return remoting.ChromotingEvent.SignalStrategyProgress.TIMED_OUT;
396     case Progress.SUCCEEDED_LATE:
397       return remoting.ChromotingEvent.SignalStrategyProgress.SUCCEEDED_LATE;
398     case Progress.FAILED_LATE:
399       return remoting.ChromotingEvent.SignalStrategyProgress.FAILED_LATE;
400     default:
401       throw new Error('Unknown signal strategy progress :=' + progress);
402   }
406  * @param {string} type
407  * @return {remoting.ChromotingEvent.ConnectionType}
408  */
409 function toConnectionType(type) {
410   switch (type) {
411     case 'direct':
412       return remoting.ChromotingEvent.ConnectionType.DIRECT;
413     case 'stun':
414       return remoting.ChromotingEvent.ConnectionType.STUN;
415     case 'relay':
416       return remoting.ChromotingEvent.ConnectionType.RELAY;
417     default:
418       throw new Error('Unknown ConnectionType :=' + type);
419   }
422 })();