@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/README.md +16 -1
- package/app.min.js +1 -1
- package/build/src/app.d.ts +13 -4
- package/build/src/app.js +170 -124
- package/build/src/app.js.map +1 -1
- package/build/src/config.d.ts +2 -91
- package/build/src/config.js +37 -29
- package/build/src/config.js.map +1 -1
- package/build/src/server.js +28 -10
- package/build/src/server.js.map +1 -1
- package/build/src/session.js +3 -2
- package/build/src/session.js.map +1 -1
- package/build/src/stats.d.ts +8 -21
- package/build/src/stats.js +67 -35
- package/build/src/stats.js.map +1 -1
- package/build/src/utils.d.ts +1 -1
- package/build/src/utils.js +9 -10
- package/build/src/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +31 -28
- package/src/app.ts +184 -136
- package/src/config.ts +35 -28
- package/src/server.ts +33 -13
- package/src/session.ts +3 -1
- package/src/stats.ts +60 -27
- package/src/utils.ts +10 -12
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
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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] =
|
|
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
|
-
|
|
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
|
|
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()
|
|
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()
|
|
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()
|
|
821
|
+
private scheduleNext() {
|
|
824
822
|
if (!this.running) {
|
|
825
823
|
return
|
|
826
824
|
}
|