- add sources.
[platform/framework/web/crosswalk.git] / src / remoting / webapp / log_to_server.js
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.
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> */
20   this.pendingEntries = [];
21   /** @type {remoting.StatsAccumulator} */
22   this.statsAccumulator = new remoting.StatsAccumulator();
23   /** @type string */
24   this.sessionId = '';
25   /** @type number */
26   this.sessionIdGenerationTime = 0;
27   /** @type number */
28   this.sessionStartTime = 0;
29 };
30
31 // Constants used for generating a session ID.
32 /** @private */
33 remoting.LogToServer.SESSION_ID_ALPHABET_ =
34     'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
35 /** @private */
36 remoting.LogToServer.SESSION_ID_LEN_ = 20;
37
38 // The maximum age of a session ID, in milliseconds.
39 remoting.LogToServer.MAX_SESSION_ID_AGE = 24 * 60 * 60 * 1000;
40
41 // The time over which to accumulate connection statistics before logging them
42 // to the server, in milliseconds.
43 remoting.LogToServer.CONNECTION_STATS_ACCUMULATE_TIME = 60 * 1000;
44
45 /**
46  * Logs a client session state change.
47  *
48  * @param {remoting.ClientSession.State} state
49  * @param {remoting.Error} connectionError
50  * @param {remoting.ClientSession.Mode} mode
51  */
52 remoting.LogToServer.prototype.logClientSessionStateChange =
53     function(state, connectionError, mode) {
54   this.maybeExpireSessionId(mode);
55   // Maybe set the session ID and start time.
56   if (remoting.LogToServer.isStartOfSession(state)) {
57     if (this.sessionId == '') {
58       this.setSessionId();
59     }
60     if (this.sessionStartTime == 0) {
61       this.sessionStartTime = new Date().getTime();
62     }
63   }
64   // Log the session state change.
65   var entry = remoting.ServerLogEntry.makeClientSessionStateChange(
66       state, connectionError, mode);
67   entry.addHostFields();
68   entry.addChromeVersionField();
69   entry.addWebappVersionField();
70   entry.addSessionIdField(this.sessionId);
71   // Maybe clear the session start time, and log the session duration.
72   if (remoting.LogToServer.shouldAddDuration(state) &&
73       (this.sessionStartTime != 0)) {
74     entry.addSessionDurationField(
75         (new Date().getTime() - this.sessionStartTime) / 1000.0);
76     if (remoting.LogToServer.isEndOfSession(state)) {
77       this.sessionStartTime = 0;
78     }
79   }
80   this.log(entry);
81   // Don't accumulate connection statistics across state changes.
82   this.logAccumulatedStatistics(mode);
83   this.statsAccumulator.empty();
84   // Maybe clear the session ID.
85   if (remoting.LogToServer.isEndOfSession(state)) {
86     this.clearSessionId();
87   }
88 };
89
90 /**
91  * Whether a session state is one of the states that occurs at the start of
92  * a session.
93  *
94  * @private
95  * @param {remoting.ClientSession.State} state
96  * @return {boolean}
97  */
98 remoting.LogToServer.isStartOfSession = function(state) {
99   return ((state == remoting.ClientSession.State.CONNECTING) ||
100       (state == remoting.ClientSession.State.INITIALIZING) ||
101       (state == remoting.ClientSession.State.CONNECTED));
102 };
103
104 /**
105  * Whether a session state is one of the states that occurs at the end of
106  * a session.
107  *
108  * @private
109  * @param {remoting.ClientSession.State} state
110  * @return {boolean}
111  */
112 remoting.LogToServer.isEndOfSession = function(state) {
113   return ((state == remoting.ClientSession.State.CLOSED) ||
114       (state == remoting.ClientSession.State.FAILED) ||
115       (state == remoting.ClientSession.State.CONNECTION_DROPPED) ||
116       (state == remoting.ClientSession.State.CONNECTION_CANCELED));
117 };
118
119 /**
120  * Whether the duration should be added to the log entry for this state.
121  *
122  * @private
123  * @param {remoting.ClientSession.State} state
124  * @return {boolean}
125  */
126 remoting.LogToServer.shouldAddDuration = function(state) {
127   // Duration is added to log entries at the end of the session, as well as at
128   // some intermediate states where it is relevant (e.g. to determine how long
129   // it took for a session to become CONNECTED).
130   return (remoting.LogToServer.isEndOfSession(state) ||
131       (state == remoting.ClientSession.State.CONNECTED));
132 };
133
134 /**
135  * Logs connection statistics.
136  * @param {Object.<string, number>} stats the connection statistics
137  * @param {remoting.ClientSession.Mode} mode
138  */
139 remoting.LogToServer.prototype.logStatistics = function(stats, mode) {
140   this.maybeExpireSessionId(mode);
141   // Store the statistics.
142   this.statsAccumulator.add(stats);
143   // Send statistics to the server if they've been accumulating for at least
144   // 60 seconds.
145   if (this.statsAccumulator.getTimeSinceFirstValue() >=
146       remoting.LogToServer.CONNECTION_STATS_ACCUMULATE_TIME) {
147     this.logAccumulatedStatistics(mode);
148   }
149 };
150
151 /**
152  * Moves connection statistics from the accumulator to the log server.
153  *
154  * If all the statistics are zero, then the accumulator is still emptied,
155  * but the statistics are not sent to the log server.
156  *
157  * @private
158  * @param {remoting.ClientSession.Mode} mode
159  */
160 remoting.LogToServer.prototype.logAccumulatedStatistics = function(mode) {
161   var entry = remoting.ServerLogEntry.makeStats(this.statsAccumulator, mode);
162   if (entry) {
163     entry.addHostFields();
164     entry.addChromeVersionField();
165     entry.addWebappVersionField();
166     entry.addSessionIdField(this.sessionId);
167     this.log(entry);
168   }
169   this.statsAccumulator.empty();
170 };
171
172 /**
173  * Sends a log entry to the server.
174  *
175  * @private
176  * @param {remoting.ServerLogEntry} entry
177  */
178 remoting.LogToServer.prototype.log = function(entry) {
179   // Send the stanza to the debug log.
180   console.log('Enqueueing log entry:');
181   entry.toDebugLog(1);
182   // Store a stanza for the entry.
183   this.pendingEntries.push(entry.toStanza());
184   // Send all pending entries to the server.
185   console.log('Sending ' + this.pendingEntries.length + ' log ' +
186               ((this.pendingEntries.length == 1) ? 'entry' : 'entries') +
187               '  to the server.');
188   var stanza = '<cli:iq to="' +
189       remoting.settings.DIRECTORY_BOT_JID + '" type="set" ' +
190       'xmlns:cli="jabber:client"><gr:log xmlns:gr="google:remoting">';
191   while (this.pendingEntries.length > 0) {
192     stanza += /** @type string */ this.pendingEntries.shift();
193   }
194   stanza += '</gr:log></cli:iq>';
195   remoting.wcsSandbox.sendIq(stanza);
196 };
197
198 /**
199  * Sets the session ID to a random string.
200  *
201  * @private
202  */
203 remoting.LogToServer.prototype.setSessionId = function() {
204   this.sessionId = remoting.LogToServer.generateSessionId();
205   this.sessionIdGenerationTime = new Date().getTime();
206 };
207
208 /**
209  * Clears the session ID.
210  *
211  * @private
212  */
213 remoting.LogToServer.prototype.clearSessionId = function() {
214   this.sessionId = '';
215   this.sessionIdGenerationTime = 0;
216 };
217
218 /**
219  * Sets a new session ID, if the current session ID has reached its maximum age.
220  *
221  * This method also logs the old and new session IDs to the server, in separate
222  * log entries.
223  *
224  * @private
225  * @param {remoting.ClientSession.Mode} mode
226  */
227 remoting.LogToServer.prototype.maybeExpireSessionId = function(mode) {
228   if ((this.sessionId != '') &&
229       (new Date().getTime() - this.sessionIdGenerationTime >=
230       remoting.LogToServer.MAX_SESSION_ID_AGE)) {
231     // Log the old session ID.
232     var entry = remoting.ServerLogEntry.makeSessionIdOld(this.sessionId, mode);
233     this.log(entry);
234     // Generate a new session ID.
235     this.setSessionId();
236     // Log the new session ID.
237     entry = remoting.ServerLogEntry.makeSessionIdNew(this.sessionId, mode);
238     this.log(entry);
239   }
240 };
241
242 /**
243  * Generates a string that can be used as a session ID.
244  *
245  * @private
246  * @return {string} a session ID
247  */
248 remoting.LogToServer.generateSessionId = function() {
249   var idArray = [];
250   for (var i = 0; i < remoting.LogToServer.SESSION_ID_LEN_; i++) {
251     var index =
252         Math.random() * remoting.LogToServer.SESSION_ID_ALPHABET_.length;
253     idArray.push(
254         remoting.LogToServer.SESSION_ID_ALPHABET_.slice(index, index + 1));
255   }
256   return idArray.join('');
257 };