@vpalmisano/webrtcperf 4.2.0 → 4.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/stats.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import axios from 'axios'
2
- import chalk from 'chalk'
3
2
  import * as events from 'events'
4
3
  import { Stats as FastStats } from 'fast-stats'
5
4
  import * as fs from 'fs'
@@ -20,6 +19,9 @@ export { FastStats }
20
19
 
21
20
  const log = logger('webrtcperf:stats')
22
21
 
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
23
+ const { default: chalk } = require('chalk-template')
24
+
23
25
  function calculateFailAmountPercentile(stat: FastStats, percentile = 95): number {
24
26
  return Math.round(stat.percentile(percentile))
25
27
  }
@@ -30,23 +32,19 @@ function calculateFailAmountPercentile(stat: FastStats, percentile = 95): number
30
32
  class StatsWriter {
31
33
  fname: string
32
34
  columns: string[]
33
- private _header_written = false
35
+ private headerWritten = false
34
36
 
35
37
  constructor(fname = 'stats.log', columns: string[]) {
36
38
  this.fname = fname
37
39
  this.columns = columns
38
40
  }
39
41
 
40
- /**
41
- * push
42
- * @param dataColumns
43
- */
44
- async push(dataColumns: string[]): Promise<void> {
45
- if (!this._header_written) {
42
+ async push(dataColumns: string[], append = true): Promise<void> {
43
+ if (!this.headerWritten || !append) {
46
44
  const data = ['datetime', ...this.columns].join(',') + '\n'
47
45
  await fs.promises.mkdir(path.dirname(this.fname), { recursive: true })
48
46
  await fs.promises.writeFile(this.fname, data)
49
- this._header_written = true
47
+ this.headerWritten = true
50
48
  }
51
49
  //
52
50
  const data = [Date.now(), ...dataColumns].join(',') + '\n'
@@ -144,7 +142,7 @@ function formatStats(s: FastStats, forWriter = false): StatsData | string[] {
144
142
  function sprintfStatsTitle(name: string): string {
145
143
  return sprintf(chalk`-- {bold %(name)s} %(fill)s\n`, {
146
144
  name,
147
- fill: '-'.repeat(100 - name.length - 4),
145
+ fill: '-'.repeat(110 - name.length - 4),
148
146
  })
149
147
  }
150
148
 
@@ -328,6 +326,7 @@ export class Stats extends events.EventEmitter {
328
326
  nextSessionId: number
329
327
  statsWriter: StatsWriter | null
330
328
  detailedStatsWriter: StatsWriter | null
329
+ detailedStatsSummaryWriter: StatsWriter | null
331
330
  private scheduler?: Scheduler
332
331
 
333
332
  private alertRules: Record<string, AlertRule> | null = null
@@ -384,18 +383,18 @@ export class Stats extends events.EventEmitter {
384
383
 
385
384
  collectedStats: Record<string, CollectedStats>
386
385
 
387
- collectedStatsConfig = {
386
+ private collectedStatsConfig = {
388
387
  url: '',
389
388
  pages: 0,
390
389
  startTime: 0,
391
390
  }
392
- externalCollectedStats = new Map<
391
+ private externalCollectedStats = new Map<
393
392
  string,
394
393
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
395
394
  { addedTime: number; externalStats: any; config: any }
396
395
  >()
397
- pushStatsInstance: axios.AxiosInstance | null = null
398
-
396
+ private pushStatsInstance: axios.AxiosInstance | null = null
397
+ private detailedStatsSummary: Record<string, Record<string, FastStats>> = {}
399
398
  private running = false
400
399
 
401
400
  /**
@@ -483,6 +482,7 @@ export class Stats extends events.EventEmitter {
483
482
 
484
483
  this.statsWriter = null
485
484
  this.detailedStatsWriter = null
485
+ this.detailedStatsSummaryWriter = null
486
486
  if (alertRules.trim()) {
487
487
  this.alertRules = json5.parse(alertRules)
488
488
  log.debug(`using alertRules: ${JSON.stringify(this.alertRules, undefined, 2)}`)
@@ -613,6 +613,11 @@ export class Stats extends events.EventEmitter {
613
613
  'trackId',
614
614
  ...this.statsNames,
615
615
  ])
616
+ this.detailedStatsSummaryWriter = new StatsWriter(this.detailedStatsPath.replace(/\.(.+)$/, '-summary.$1'), [
617
+ 'participantName',
618
+ 'trackId',
619
+ ...this.statsNames,
620
+ ])
616
621
  }
617
622
 
618
623
  if (this.prometheusPushgateway) {
@@ -819,8 +824,7 @@ export class Stats extends events.EventEmitter {
819
824
  collectedStats.all.push(obj)
820
825
  } else {
821
826
  for (const [key, value] of Object.entries(obj)) {
822
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
823
- if (typeof value === 'number' && isFinite(value as any)) {
827
+ if (typeof value === 'number' && isFinite(value)) {
824
828
  collectedStats.all.push(value)
825
829
  // Push host label.
826
830
  const { trackId, hostName, participantName } = parseRtStatKey(key)
@@ -953,7 +957,7 @@ export class Stats extends events.EventEmitter {
953
957
 
954
958
  async writeDetailedStats() {
955
959
  if (!this.detailedStatsWriter) return
956
- const participantTrackStats = new Map<string, Record<string, string>>()
960
+ const participantTrackStats = new Map<string, Record<string, number>>()
957
961
  Object.entries(this.collectedStats).forEach(([name, stats]) => {
958
962
  Object.entries(stats.byParticipantAndTrack).forEach(([label, value]) => {
959
963
  let stats = participantTrackStats.get(label)
@@ -961,19 +965,47 @@ export class Stats extends events.EventEmitter {
961
965
  stats = {}
962
966
  participantTrackStats.set(label, stats)
963
967
  }
964
- stats[name] = toPrecision(value, 6)
968
+ stats[name] = value
965
969
  })
966
970
  })
967
971
  for (const [label, trackStats] of participantTrackStats.entries()) {
968
972
  const [participantName, trackId] = label.split(':', 2)
973
+ if (!this.detailedStatsSummary[label]) {
974
+ this.detailedStatsSummary[label] = {}
975
+ }
976
+ const summary = this.detailedStatsSummary[label]
969
977
  const values = [participantName, trackId]
970
978
  for (const name of this.statsNames) {
971
- values.push(trackStats[name] ?? '')
979
+ values.push(trackStats[name] !== undefined ? toPrecision(trackStats[name], 6) : '')
980
+ // Update the summary stats.
981
+ if (!summary[name]) {
982
+ summary[name] = new FastStats({ store_data: false })
983
+ }
984
+ const stat = summary[name]
985
+ if (trackStats[name] !== undefined) {
986
+ stat.push(trackStats[name])
987
+ }
972
988
  }
973
989
  await this.detailedStatsWriter.push(values)
974
990
  }
975
991
  }
976
992
 
993
+ async writeDetailedStatsSummary() {
994
+ if (!this.detailedStatsSummaryWriter) return
995
+ let append = false
996
+ for (const label of Object.keys(this.detailedStatsSummary)) {
997
+ const summary = this.detailedStatsSummary[label]
998
+ const [participantName, trackId] = label.split(':', 2)
999
+ const values = [participantName, trackId]
1000
+ for (const name of this.statsNames) {
1001
+ const stat = summary[name]
1002
+ values.push(stat?.length > 0 ? toPrecision(stat.amean(), 6) : '')
1003
+ }
1004
+ await this.detailedStatsSummaryWriter.push(values, append)
1005
+ append = true
1006
+ }
1007
+ }
1008
+
977
1009
  /**
978
1010
  * addCollectedStats
979
1011
  * @param id
@@ -1650,17 +1682,13 @@ export class Stats extends events.EventEmitter {
1650
1682
  }
1651
1683
  }
1652
1684
 
1653
- /**
1654
- * Stop the stats collector and the added Sessions.
1655
- */
1656
- async stop(): Promise<void> {
1657
- if (!this.running) {
1658
- return
1659
- }
1685
+ async stop() {
1686
+ if (!this.running) return
1660
1687
  this.running = false
1661
1688
  log.debug('stop')
1689
+
1662
1690
  if (this.scheduler) {
1663
- this.scheduler.stop()
1691
+ await this.scheduler.stop()
1664
1692
  this.scheduler = undefined
1665
1693
  }
1666
1694
 
@@ -1674,7 +1702,12 @@ export class Stats extends events.EventEmitter {
1674
1702
  }
1675
1703
  this.sessions.clear()
1676
1704
 
1705
+ await this.writeDetailedStatsSummary()
1706
+
1677
1707
  this.statsWriter = null
1708
+ this.detailedStatsWriter = null
1709
+ this.detailedStatsSummaryWriter = null
1710
+ this.detailedStatsSummary = {}
1678
1711
 
1679
1712
  // delete metrics
1680
1713
  if (this.gateway) {
package/src/utils.ts CHANGED
@@ -291,7 +291,6 @@ export async function randomActivateAudio(
291
291
  pages = pages.concat(sessionPages)
292
292
  }
293
293
  }
294
- // Remove pages with no audio tracks.
295
294
  for (const [i, page] of pages.entries()) {
296
295
  if (!page) {
297
296
  continue
@@ -307,15 +306,8 @@ export async function randomActivateAudio(
307
306
  }
308
307
  }
309
308
  const pagesWithAudio: Page[] = pages.filter(p => !!p)
310
- //
311
309
  const index = Math.floor(Math.random() * pagesWithAudio.length)
312
310
  const enable = Math.round(100 * Math.random()) <= randomAudioProbability
313
- log.debug('randomActivateAudio %j', {
314
- pages: pagesWithAudio.length,
315
- randomAudioProbability,
316
- index,
317
- enable,
318
- })
319
311
  for (const [i, page] of pagesWithAudio.entries()) {
320
312
  try {
321
313
  if (i === index) {
@@ -613,7 +605,8 @@ SIGNALS.forEach(event =>
613
605
  export async function checkChromeExecutable(): Promise<string> {
614
606
  // eslint-disable-next-line @typescript-eslint/no-require-imports
615
607
  const { loadConfig } = require('./config')
616
- const config = await loadConfig()
608
+ const configs = await loadConfig()
609
+ const config = configs[0]
617
610
  const cacheDir = path.join(os.homedir(), '.webrtcperf/chrome')
618
611
 
619
612
  const fixSemVer = (v: string) => v.split('.').slice(0, 3).join('.')
@@ -806,21 +799,26 @@ export class Scheduler {
806
799
  log.debug(`[${this.name}-scheduler] constructor interval=${this.interval}ms`)
807
800
  }
808
801
 
809
- start(): void {
802
+ start() {
810
803
  log.debug(`[${this.name}-scheduler] start`)
811
804
  this.running = true
812
805
  this.scheduleNext()
813
806
  }
814
807
 
815
- stop(): void {
808
+ async stop() {
816
809
  log.debug(`[${this.name}-scheduler] stop`)
817
810
  this.running = false
818
811
  if (this.statsTimeoutId) {
819
812
  clearTimeout(this.statsTimeoutId)
820
813
  }
814
+ try {
815
+ await this.callback(Date.now())
816
+ } catch (err) {
817
+ log.error(`[${this.name}-scheduler] stop callback error: ${(err as Error).stack}`, err)
818
+ }
821
819
  }
822
820
 
823
- private scheduleNext(): void {
821
+ private scheduleNext() {
824
822
  if (!this.running) {
825
823
  return
826
824
  }