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 || {};
14 * This class defines a remoting.Logger implementation that generates
15 * log entries in the format of remoting.ChromotingEvent.
17 * @param {remoting.ChromotingEvent.Role} role
18 * @param {function(!Object)} writeLogEntry
21 * @implements {remoting.Logger}
23 remoting.SessionLogger = function(role, writeLogEntry) {
27 this.writeLogEntry_ = writeLogEntry;
29 this.statsAccumulator_ = new remoting.StatsAccumulator();
33 this.sessionIdGenerationTime_ = 0;
35 this.sessionStartTime_ = new Date().getTime();
36 /** @private {remoting.ChromotingEvent.ConnectionType} */
38 /** @private {remoting.ChromotingEvent.SessionEntryPoint} */
40 /** @private {remoting.ChromotingEvent.SessionState} */
41 this.previousSessionState_;
43 this.authTotalTime_ = 0;
45 this.hostVersion_ = '';
48 this.mode_ = remoting.ChromotingEvent.Mode.ME2ME;
54 * @param {remoting.ChromotingEvent.SessionEntryPoint} entryPoint
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) {
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);
98 /** @override {remoting.Logger} */
99 remoting.SessionLogger.prototype.logClientSessionStateChange = function(
100 state, connectionError, xmppError) {
101 this.logSessionStateChange(
102 toSessionState(state),
103 toConnectionError(connectionError),
108 * @param {remoting.ChromotingEvent.SessionState} state
109 * @param {remoting.ChromotingEvent.ConnectionError} error
110 * @param {remoting.ChromotingEvent.XmppError=} opt_XmppError
112 remoting.SessionLogger.prototype.logSessionStateChange = function(
113 state, error, opt_XmppError) {
114 this.maybeExpireSessionId_();
116 var entry = this.makeSessionStateChange_(
118 /** @type {?remoting.ChromotingEvent.XmppError} */ (opt_XmppError));
119 entry.previous_session_state = this.previousSessionState_;
120 this.previousSessionState_ = state;
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
136 if (this.statsAccumulator_.getTimeSinceFirstValue() >=
137 remoting.Logger.CONNECTION_STATS_ACCUMULATE_TIME) {
138 this.logAccumulatedStatistics_();
143 * @param {remoting.ChromotingEvent.SessionState} state
144 * @param {remoting.ChromotingEvent.ConnectionError} error
145 * @param {?remoting.ChromotingEvent.XmppError} xmppError
146 * @return {remoting.ChromotingEvent}
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;
160 this.fillEvent_(entry);
165 * @return {remoting.ChromotingEvent}
168 remoting.SessionLogger.prototype.makeSessionIdNew_ = function() {
169 var entry = new remoting.ChromotingEvent(
170 remoting.ChromotingEvent.Type.SESSION_ID_NEW);
171 this.fillEvent_(entry);
176 * @return {remoting.ChromotingEvent}
179 remoting.SessionLogger.prototype.makeSessionIdOld_ = function() {
180 var entry = new remoting.ChromotingEvent(
181 remoting.ChromotingEvent.Type.SESSION_ID_OLD);
182 this.fillEvent_(entry);
187 * @return {remoting.ChromotingEvent}
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;
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.
215 remoting.SessionLogger.prototype.logAccumulatedStatistics_ = function() {
216 var entry = this.makeStats_();
220 this.statsAccumulator_.empty();
224 * @param {remoting.ChromotingEvent} entry
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_;
239 entry.host_version = this.hostVersion_;
243 * Sends a log entry to the server.
244 * @param {remoting.ChromotingEvent} entry
247 remoting.SessionLogger.prototype.log_ = function(entry) {
248 this.writeLogEntry_(/** @type {!Object} */ (base.deepCopy(entry)));
252 * Sets the session ID to a random string.
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.
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
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_();
287 // Generate a new session ID.
288 this.setSessionId_();
289 // Log the new session ID.
290 entry = this.makeSessionIdNew_();
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}
306 function toSessionState(state) {
307 var SessionState = remoting.ChromotingEvent.SessionState;
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;
328 throw new Error('Unknown session state : ' + state);
333 * @param {remoting.Error} error
334 * @return {remoting.ChromotingEvent.ConnectionError}
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;
364 throw new Error('Unknown error Tag : ' + error.getTag());
369 * @param {remoting.SignalStrategy.Type} type
370 * @return {remoting.ChromotingEvent.SignalStrategyType}
372 function toSignalStrategyType(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;
379 throw new Error('Unknown signal strategy type : ' + type);
384 * @param {remoting.FallbackSignalStrategy.Progress} progress
385 * @return {remoting.ChromotingEvent.SignalStrategyProgress}
387 function toSignalStrategyProgress(progress) {
388 var Progress = remoting.FallbackSignalStrategy.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;
401 throw new Error('Unknown signal strategy progress :=' + progress);
406 * @param {string} type
407 * @return {remoting.ChromotingEvent.ConnectionType}
409 function toConnectionType(type) {
412 return remoting.ChromotingEvent.ConnectionType.DIRECT;
414 return remoting.ChromotingEvent.ConnectionType.STUN;
416 return remoting.ChromotingEvent.ConnectionType.RELAY;
418 throw new Error('Unknown ConnectionType :=' + type);