You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AnalyticsEvents.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /**
  2. * This class exports constants and factory methods related to the analytics
  3. * API provided by AnalyticsAdapter. In order for entries in a database to be
  4. * somewhat easily traceable back to the code which produced them, events sent
  5. * through analytics should be defined here.
  6. *
  7. * Since the AnalyticsAdapter API can be used in different ways, for some events
  8. * it is more convenient to just define the event name as a constant. For other
  9. * events a factory function is easier.
  10. *
  11. * A general approach for adding a new event:
  12. * 1. Determine the event type: track, UI, page, or operational. If in doubt use
  13. * operational.
  14. * 2. Determine whether the event is related to other existing events, and
  15. * which fields are desired to be set: name, action, actionSubject, source.
  16. * 3. If the name is sufficient (the other fields are not important), use a
  17. * constant. Otherwise use a factory function.
  18. *
  19. * Note that the AnalyticsAdapter uses the events passed to its functions for
  20. * its own purposes, and might modify them. Because of this, factory functions
  21. * should create new objects.
  22. *
  23. */
  24. /**
  25. * The constant which identifies an event of type "operational".
  26. * @type {string}
  27. */
  28. export const TYPE_OPERATIONAL = 'operational';
  29. /**
  30. * The constant which identifies an event of type "page".
  31. * @type {string}
  32. */
  33. export const TYPE_PAGE = 'page';
  34. /**
  35. * The constant which identifies an event of type "track".
  36. * @type {string}
  37. */
  38. export const TYPE_TRACK = 'track';
  39. /**
  40. * The constant which identifies an event of type "ui".
  41. * @type {string}
  42. */
  43. export const TYPE_UI = 'ui';
  44. /**
  45. * The "action" value for Jingle events which indicates that the Jingle session
  46. * was restarted (TODO: verify/fix the documentation)
  47. * @type {string}
  48. */
  49. export const ACTION_JINGLE_RESTART = 'restart';
  50. /**
  51. * The "action" value for Jingle events which indicates that a session-accept
  52. * timed out (TODO: verify/fix the documentation)
  53. * @type {string}
  54. */
  55. export const ACTION_JINGLE_SA_TIMEOUT = 'session-accept.timeout';
  56. /**
  57. * The "action" value for Jingle events which indicates that a session-initiate
  58. * was received.
  59. * @type {string}
  60. */
  61. export const ACTION_JINGLE_SI_RECEIVED = 'session-initiate.received';
  62. /**
  63. * The "action" value for Jingle events which indicates that a session-initiate
  64. * not arrived within a timeout (the value is specified in
  65. * the {@link JingleSessionPC}.
  66. * @type {string}
  67. */
  68. export const ACTION_JINGLE_SI_TIMEOUT = 'session-initiate.timeout';
  69. /**
  70. * A constant for the "terminate" action for Jingle events. TODO: verify/fix
  71. * the documentation)
  72. * @type {string}
  73. */
  74. export const ACTION_JINGLE_TERMINATE = 'terminate';
  75. /**
  76. * The "action" value for Jingle events which indicates that a transport-replace
  77. * was received.
  78. * @type {string}
  79. */
  80. export const ACTION_JINGLE_TR_RECEIVED
  81. = 'transport-replace.received';
  82. /**
  83. * The "action" value for Jingle events which indicates that a transport-replace
  84. * succeeded (TODO: verify/fix the documentation)
  85. * @type {string}
  86. */
  87. export const ACTION_JINGLE_TR_SUCCESS
  88. = 'transport-replace.success';
  89. /**
  90. * The "action" value for P2P events which indicates that P2P session initiate message has been rejected by the client
  91. * because the mandatory requirements were not met.
  92. * @type {string}
  93. */
  94. export const ACTION_P2P_DECLINED = 'decline';
  95. /**
  96. * The "action" value for P2P events which indicates that a connection was
  97. * established (TODO: verify/fix the documentation)
  98. * @type {string}
  99. */
  100. export const ACTION_P2P_ESTABLISHED = 'established';
  101. /**
  102. * The "action" value for P2P events which indicates that something failed.
  103. * @type {string}
  104. */
  105. export const ACTION_P2P_FAILED = 'failed';
  106. /**
  107. * The "action" value for P2P events which indicates that a switch to
  108. * jitsi-videobridge happened.
  109. * @type {string}
  110. */
  111. export const ACTION_P2P_SWITCH_TO_JVB = 'switch.to.jvb';
  112. /**
  113. * The name of an event which indicates an available device. We send one such
  114. * event per available device once when the available devices are first known,
  115. * and every time that they change
  116. * @type {string}
  117. *
  118. * Properties:
  119. * audio_input_device_count: the number of audio input devices available at
  120. * the time the event was sent.
  121. * audio_output_device_count: the number of audio output devices available
  122. * at the time the event was sent.
  123. * video_input_device_count: the number of video input devices available at
  124. * the time the event was sent.
  125. * video_output_device_count: the number of video output devices available
  126. * at the time the event was sent.
  127. * device_id: an identifier of the device described in this event.
  128. * device_group_id:
  129. * device_kind: one of 'audioinput', 'audiooutput', 'videoinput' or
  130. * 'videooutput'.
  131. * device_label: a string which describes the device.
  132. */
  133. export const AVAILABLE_DEVICE = 'available.device';
  134. /**
  135. * This appears to be fired only in certain cases when the XMPP connection
  136. * disconnects (and it was intentional?). It is currently never observed to
  137. * fire in production.
  138. *
  139. * TODO: document
  140. *
  141. * Properties:
  142. * message: an error message
  143. */
  144. export const CONNECTION_DISCONNECTED = 'connection.disconnected';
  145. /**
  146. * Indicates that the user of the application provided feedback in terms of a
  147. * rating (an integer from 1 to 5) and an optional comment.
  148. * Properties:
  149. * value: the user's rating (an integer from 1 to 5)
  150. * comment: the user's comment
  151. */
  152. export const FEEDBACK = 'feedback';
  153. /**
  154. * Indicates the duration of a particular phase of the ICE connectivity
  155. * establishment.
  156. *
  157. * Properties:
  158. * phase: the ICE phase (e.g. 'gathering', 'checking', 'establishment')
  159. * value: the duration in milliseconds.
  160. * p2p: whether the associated ICE connection is p2p or towards a
  161. * jitsi-videobridge
  162. * initiator: whether the local Jingle peer is the initiator or responder
  163. * in the Jingle session. XXX we probably actually care about the ICE
  164. * role (controlling vs controlled), and we assume that this correlates
  165. * with the Jingle initiator.
  166. */
  167. export const ICE_DURATION = 'ice.duration';
  168. /**
  169. * Indicates the difference in milliseconds between the ICE establishment time
  170. * for the P2P and JVB connections (e.g. a value of 10 would indicate that the
  171. * P2P connection took 10ms more than JVB connection to establish).
  172. *
  173. * Properties:
  174. * value: the difference in establishment durations in milliseconds.
  175. *
  176. */
  177. export const ICE_ESTABLISHMENT_DURATION_DIFF
  178. = 'ice.establishment.duration.diff';
  179. /**
  180. * Indicates that the ICE state has changed.
  181. *
  182. * Properties:
  183. * state: the ICE state which was entered (e.g. 'checking', 'connected',
  184. * 'completed', etc).
  185. * value: the time in milliseconds (as reported by
  186. * window.performance.now()) that the state change occurred.
  187. * p2p: whether the associated ICE connection is p2p or towards a
  188. * jitsi-videobridge
  189. * signalingState: The signaling state of the associated PeerConnection
  190. * reconnect: whether the associated Jingle session is in the process of
  191. * reconnecting (or is it ICE? TODO: verify/fix the documentation)
  192. */
  193. export const ICE_STATE_CHANGED = 'ice.state.changed';
  194. /**
  195. * Indicates that no bytes have been sent for the track.
  196. *
  197. * Properties:
  198. * mediaType: the media type of the local track ('audio' or 'video').
  199. */
  200. export const NO_BYTES_SENT = 'track.no-bytes-sent';
  201. /**
  202. * Indicates that a track was unmuted (?).
  203. *
  204. * Properties:
  205. * mediaType: the media type of the local track ('audio' or 'video').
  206. * trackType: the type of the track ('local' or 'remote').
  207. * value: TODO: document
  208. */
  209. export const TRACK_UNMUTED = 'track.unmuted';
  210. /**
  211. * Creates an operational event which indicates that we have received a
  212. * "bridge down" event from jicofo.
  213. */
  214. export const createBridgeDownEvent = function() {
  215. const bridgeDown = 'bridge.down';
  216. return {
  217. action: bridgeDown,
  218. actionSubject: bridgeDown,
  219. type: TYPE_OPERATIONAL
  220. };
  221. };
  222. /**
  223. * Creates an event which indicates that the XMPP connection failed
  224. * @param errorType TODO
  225. * @param errorMessage TODO
  226. * @param detail connection failed details.
  227. */
  228. export const createConnectionFailedEvent
  229. = function(errorType, errorMessage, details) {
  230. return {
  231. type: TYPE_OPERATIONAL,
  232. action: 'connection.failed',
  233. attributes: {
  234. 'error_type': errorType,
  235. 'error_message': errorMessage,
  236. ...details
  237. }
  238. };
  239. };
  240. /**
  241. * Creates an operational event which indicates that a particular connection
  242. * stage was reached (i.e. the XMPP connection transitioned to the "connected"
  243. * state).
  244. *
  245. * @param stage the stage which was reached
  246. * @param attributes additional attributes for the event. This should be an
  247. * object with a "value" property indicating a timestamp in milliseconds
  248. * relative to the beginning of the document's lifetime.
  249. *
  250. */
  251. export const createConnectionStageReachedEvent = function(stage, attributes) {
  252. const action = 'connection.stage.reached';
  253. return {
  254. action,
  255. actionSubject: stage,
  256. attributes,
  257. source: action,
  258. type: TYPE_OPERATIONAL
  259. };
  260. };
  261. /**
  262. * Creates an operational event for the end-to-end round trip time to a
  263. * specific remote participant.
  264. * @param participantId the ID of the remote participant.
  265. * @param region the region of the remote participant
  266. * @param rtt the rtt
  267. */
  268. export const createE2eRttEvent = function(participantId, region, rtt) {
  269. const attributes = {
  270. 'participant_id': participantId,
  271. region,
  272. rtt
  273. };
  274. return {
  275. attributes,
  276. name: 'e2e_rtt',
  277. type: TYPE_OPERATIONAL
  278. };
  279. };
  280. /**
  281. * Creates an event which indicates that the focus has left the MUC.
  282. */
  283. export const createFocusLeftEvent = function() {
  284. const action = 'focus.left';
  285. return {
  286. action,
  287. actionSubject: action,
  288. type: TYPE_OPERATIONAL
  289. };
  290. };
  291. /**
  292. * Creates an event related to a getUserMedia call.
  293. *
  294. * @param action the type of the result that the event represents: 'error',
  295. * 'success', 'warning', etc.
  296. * @param attributes the attributes to attach to the event.
  297. * @returns {{type: string, source: string, name: string}}
  298. */
  299. export const createGetUserMediaEvent = function(action, attributes = {}) {
  300. return {
  301. type: TYPE_OPERATIONAL,
  302. source: 'get.user.media',
  303. action,
  304. attributes
  305. };
  306. };
  307. /**
  308. * Creates an event related to remote participant connection status changes.
  309. *
  310. * @param attributes the attributes to attach to the event.
  311. * @returns {{type: string, source: string, name: string}}
  312. */
  313. export const createParticipantConnectionStatusEvent = function(attributes = {}) {
  314. const action = 'duration';
  315. return {
  316. type: TYPE_OPERATIONAL,
  317. source: 'peer.conn.status',
  318. action,
  319. attributes
  320. };
  321. };
  322. /**
  323. * Creates an event for a Jingle-related event.
  324. * @param action the action of the event
  325. * @param attributes attributes to add to the event.
  326. */
  327. export const createJingleEvent = function(action, attributes = {}) {
  328. return {
  329. type: TYPE_OPERATIONAL,
  330. action,
  331. source: 'jingle',
  332. attributes
  333. };
  334. };
  335. /**
  336. * Creates an event which indicates that a local track was not able to read
  337. * data from its source (a camera or a microphone).
  338. *
  339. * @param mediaType {String} the media type of the local track ('audio' or
  340. * 'video').
  341. */
  342. export const createNoDataFromSourceEvent = function(mediaType, value) {
  343. return {
  344. attributes: {
  345. 'media_type': mediaType,
  346. value
  347. },
  348. action: 'track.no.data.from.source',
  349. type: TYPE_OPERATIONAL
  350. };
  351. };
  352. /**
  353. * Creates an event for a p2p-related event.
  354. * @param action the action of the event
  355. * @param attributes attributes to add to the event.
  356. */
  357. export const createP2PEvent = function(action, attributes = {}) {
  358. return {
  359. type: TYPE_OPERATIONAL,
  360. action,
  361. source: 'p2p',
  362. attributes
  363. };
  364. };
  365. /**
  366. * Indicates that we received a remote command to mute.
  367. */
  368. export const createRemotelyMutedEvent = function() {
  369. return {
  370. type: TYPE_OPERATIONAL,
  371. action: 'remotely.muted'
  372. };
  373. };
  374. /**
  375. * Creates an event which contains RTP statistics such as RTT and packet loss.
  376. *
  377. * All average RTP stats are currently reported under 1 event name, but with
  378. * different properties that allows to distinguish between a P2P call, a
  379. * call relayed through TURN or the JVB, and multiparty vs 1:1.
  380. *
  381. * The structure of the event is:
  382. *
  383. * {
  384. * p2p: true,
  385. * conferenceSize: 2,
  386. * localCandidateType: "relay",
  387. * remoteCandidateType: "relay",
  388. * transportType: "udp",
  389. *
  390. * // Average RTT of 200ms
  391. * "rtt.avg": 200,
  392. * "rtt.samples": "[100, 200, 300]",
  393. *
  394. * // Average packet loss of 10%
  395. * "packet.loss.avg": 10,
  396. * "packet.loss.samples": '[5, 10, 15]'
  397. *
  398. * // Difference in milliseconds in the end-to-end RTT between p2p and jvb.
  399. * // The e2e RTT through jvb is 15ms shorter:
  400. * "rtt.diff": 15,
  401. *
  402. * // End-to-end RTT through JVB is ms.
  403. * "end2end.rtt.avg" = 100
  404. * }
  405. *
  406. * Note that the value of the "samples" properties are (JSON encoded) strings,
  407. * and not JSON arrays, as events' attributes can not be nested. The samples are
  408. * currently included for debug purposes only and can be removed anytime soon
  409. * from the structure.
  410. *
  411. * Also note that not all of values are present in each event, as values are
  412. * obtained and calculated as part of different process/event pipe. For example
  413. * {@link ConnectionAvgStats} instances are doing the reports for each
  414. * {@link TraceablePeerConnection} and work independently from the main stats
  415. * pipe.
  416. */
  417. export const createRtpStatsEvent = function(attributes) {
  418. return {
  419. type: TYPE_OPERATIONAL,
  420. action: 'rtp.stats',
  421. attributes
  422. };
  423. };
  424. /**
  425. * Creates an event which contains the round trip time (RTT) to a set of
  426. * regions.
  427. *
  428. * @param attributes
  429. * @returns {{type: string, action: string, attributes: *}}
  430. */
  431. export const createRttByRegionEvent = function(attributes) {
  432. return {
  433. type: TYPE_OPERATIONAL,
  434. action: 'rtt.by.region',
  435. attributes
  436. };
  437. };
  438. /**
  439. * Creates an event which contains information about the audio output problem (the user id of the affected participant,
  440. * the local audio levels and the remote audio levels that triggered the event).
  441. *
  442. * @param {string} userID - The user id of the affected participant.
  443. * @param {*} localAudioLevels - The local audio levels.
  444. * @param {*} remoteAudioLevels - The audio levels received from the participant.
  445. */
  446. export function createAudioOutputProblemEvent(userID, localAudioLevels, remoteAudioLevels) {
  447. return {
  448. type: TYPE_OPERATIONAL,
  449. action: 'audio.output.problem',
  450. attributes: {
  451. userID,
  452. localAudioLevels,
  453. remoteAudioLevels
  454. }
  455. };
  456. }
  457. /**
  458. * Creates an event which contains an information related to the bridge channel close event.
  459. *
  460. * @param {string} code - A code from {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
  461. * @param {string} reason - A string which describes the reason for closing the bridge channel.
  462. * @returns {{type: string, action: string, attributes: { code: string, reason: string }}}
  463. */
  464. export const createBridgeChannelClosedEvent = function(code, reason) {
  465. return {
  466. type: TYPE_OPERATIONAL,
  467. action: 'bridge-channel.error',
  468. attributes: {
  469. code,
  470. reason
  471. }
  472. };
  473. };
  474. /**
  475. * Creates an event which indicates the Time To First Media (TTFM).
  476. * It is measured in milliseconds relative to the beginning of the document's
  477. * lifetime (i.e. the origin used by window.performance.now()), and it excludes
  478. * the following:
  479. * 1. The delay due to getUserMedia()
  480. * 2. The period between the MUC being joined and the reception of the Jingle
  481. * session-initiate from jicofo. This is because jicofo will not start a Jingle
  482. * session until there are at least 2 participants in the room.
  483. *
  484. * @param attributes the attributes to add to the event. Currently used fields:
  485. * mediaType: the media type of the local track ('audio' or 'video').
  486. * muted: whether the track has ever been muted (?)
  487. * value: the TTMF in milliseconds.
  488. */
  489. export const createTtfmEvent = function(attributes) {
  490. return createConnectionStageReachedEvent('ttfm', attributes);
  491. };