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.

PerformanceObserverStats.js 3.4KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { getLogger } from '@jitsi/logger';
  2. import * as StatisticsEvents from '../../service/statistics/Events';
  3. import { RunningAverage } from '../util/MathUtil';
  4. const logger = getLogger(__filename);
  5. const MILLI_SECONDS = 1000;
  6. const SECONDS = 60;
  7. /**
  8. * This class creates an observer that monitors browser's performance measurement events
  9. * as they are recorded in the browser's performance timeline and computes an average and
  10. * a maximum value for the long task events. Tasks are classified as long tasks if they take
  11. * longer than 50ms to execute on the main thread.
  12. */
  13. export class PerformanceObserverStats {
  14. /**
  15. * Creates a new instance of Performance observer statistics.
  16. *
  17. * @param {*} emitter Event emitter for emitting stats periodically
  18. * @param {*} statsInterval interval for calculating the stats
  19. */
  20. constructor(emitter, statsInterval) {
  21. this.eventEmitter = emitter;
  22. this.longTasks = 0;
  23. this.maxDuration = 0;
  24. this.performanceStatsInterval = statsInterval;
  25. this.stats = new RunningAverage();
  26. }
  27. /**
  28. * Obtains the average rate of long tasks observed per min and the
  29. * duration of the longest task recorded by the observer.
  30. * @returns {Object}
  31. */
  32. getLongTasksStats() {
  33. return {
  34. avgRatePerMinute: (this.stats.getAverage() * SECONDS).toFixed(2), // calc rate per min
  35. maxDurationMs: this.maxDuration
  36. };
  37. }
  38. /**
  39. * Starts the performance observer by registering the callback function
  40. * that calculates the performance statistics periodically.
  41. * @returns {void}
  42. */
  43. startObserver() {
  44. // Create a handler for when the long task event is fired.
  45. this.longTaskEventHandler = list => {
  46. const entries = list.getEntries();
  47. for (const task of entries) {
  48. this.longTasks++;
  49. this.maxDuration = Math.max(this.maxDuration, task.duration).toFixed(3);
  50. }
  51. };
  52. // Create an observer for monitoring long tasks.
  53. logger.info('Creating a Performance Observer for monitoring Long Tasks');
  54. this.observer = new PerformanceObserver(this.longTaskEventHandler);
  55. this.observer.observe({ type: 'longtask',
  56. buffered: true });
  57. const startTime = Date.now();
  58. // Calculate the average # of events/sec and emit a stats event.
  59. this.longTasksIntervalId = setInterval(() => {
  60. const now = Date.now();
  61. const interval = this._lastTimeStamp
  62. ? (now - this._lastTimeStamp) / MILLI_SECONDS
  63. : (now - startTime) / MILLI_SECONDS;
  64. const rate = this.longTasks / interval;
  65. this.stats.addNext(rate);
  66. this.eventEmitter.emit(
  67. StatisticsEvents.LONG_TASKS_STATS, this.getLongTasksStats());
  68. // Reset the counter and start counting events again.
  69. this.longTasks = 0;
  70. this._lastTimeStamp = Date.now();
  71. }, this.performanceStatsInterval);
  72. }
  73. /**
  74. * Stops the performance observer.
  75. * @returns {void}
  76. */
  77. stopObserver() {
  78. this.observer && this.observer.disconnect();
  79. this.longTaskEventHandler = null;
  80. if (this.longTasksIntervalId) {
  81. clearInterval(this.longTasksIntervalId);
  82. this.longTasksIntervalId = null;
  83. }
  84. }
  85. }