aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/assets/player/peertube-player.ts2
-rw-r--r--client/src/assets/player/shared/control-bar/p2p-info-button.ts20
-rw-r--r--client/src/assets/player/shared/metrics/metrics-plugin.ts25
-rw-r--r--client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts36
-rw-r--r--client/src/assets/player/shared/player-options-builder/hls-options-builder.ts2
-rw-r--r--client/src/assets/player/shared/stats/stats-card.ts31
-rw-r--r--client/src/assets/player/shared/web-video/web-video-plugin.ts2
-rw-r--r--client/src/assets/player/types/peertube-videojs-typings.ts7
-rw-r--r--config/dev.yaml4
-rw-r--r--server/lib/opentelemetry/metric-helpers/playback-metrics.ts22
-rw-r--r--server/middlewares/validators/metrics.ts3
-rw-r--r--server/tests/api/check-params/metrics.ts11
-rw-r--r--server/tests/api/server/open-telemetry.ts39
-rw-r--r--shared/models/metrics/playback-metric-create.model.ts4
-rw-r--r--support/doc/api/openapi.yaml6
15 files changed, 141 insertions, 73 deletions
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
index ebb79247a..69ca1a566 100644
--- a/client/src/assets/player/peertube-player.ts
+++ b/client/src/assets/player/peertube-player.ts
@@ -203,7 +203,7 @@ export class PeerTubePlayer {
203 203
204 this.player.one('error', () => handleError()) 204 this.player.one('error', () => handleError())
205 205
206 this.player.on('p2p-info', (_, data: PlayerNetworkInfo) => { 206 this.player.on('network-info', (_, data: PlayerNetworkInfo) => {
207 if (data.source !== 'p2p-media-loader' || isNaN(data.bandwidthEstimate)) return 207 if (data.source !== 'p2p-media-loader' || isNaN(data.bandwidthEstimate)) return
208 208
209 saveAverageBandwidth(data.bandwidthEstimate) 209 saveAverageBandwidth(data.bandwidthEstimate)
diff --git a/client/src/assets/player/shared/control-bar/p2p-info-button.ts b/client/src/assets/player/shared/control-bar/p2p-info-button.ts
index 4177b3280..d2d2b6c99 100644
--- a/client/src/assets/player/shared/control-bar/p2p-info-button.ts
+++ b/client/src/assets/player/shared/control-bar/p2p-info-button.ts
@@ -39,15 +39,14 @@ class P2PInfoButton extends Button {
39 subDivP2P.appendChild(peersText) 39 subDivP2P.appendChild(peersText)
40 40
41 const subDivHttp = videojs.dom.createEl('div', { className: 'vjs-peertube-hidden' }) as HTMLElement 41 const subDivHttp = videojs.dom.createEl('div', { className: 'vjs-peertube-hidden' }) as HTMLElement
42 const subDivHttpText = videojs.dom.createEl('span', { 42 const subDivHttpText = videojs.dom.createEl('span', { className: 'http-fallback' })
43 className: 'http-fallback',
44 textContent: 'HTTP'
45 })
46 43
47 subDivHttp.appendChild(subDivHttpText) 44 subDivHttp.appendChild(subDivHttpText)
48 div.appendChild(subDivHttp) 45 div.appendChild(subDivHttp)
49 46
50 this.player_.on('p2p-info', (_event: any, data: PlayerNetworkInfo) => { 47 this.player_.on('network-info', (_event: any, data: PlayerNetworkInfo) => {
48 if (!data.p2p) return
49
51 subDivP2P.className = 'vjs-peertube-displayed' 50 subDivP2P.className = 'vjs-peertube-displayed'
52 subDivHttp.className = 'vjs-peertube-hidden' 51 subDivHttp.className = 'vjs-peertube-hidden'
53 52
@@ -58,7 +57,7 @@ class P2PInfoButton extends Button {
58 const uploadSpeed = bytes(p2pStats.uploadSpeed) 57 const uploadSpeed = bytes(p2pStats.uploadSpeed)
59 const totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded) 58 const totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded)
60 const totalUploaded = bytes(p2pStats.uploaded) 59 const totalUploaded = bytes(p2pStats.uploaded)
61 const numPeers = p2pStats.numPeers 60 const numPeers = p2pStats.peersWithWebSeed
62 61
63 subDivP2P.title = this.player().localize('Total downloaded: ') + totalDownloaded.join(' ') + '\n' 62 subDivP2P.title = this.player().localize('Total downloaded: ') + totalDownloaded.join(' ') + '\n'
64 63
@@ -85,8 +84,13 @@ class P2PInfoButton extends Button {
85 subDivP2P.className = 'vjs-peertube-displayed' 84 subDivP2P.className = 'vjs-peertube-displayed'
86 }) 85 })
87 86
88 this.player_.on('http-info', (_event, data: PlayerNetworkInfo) => { 87 this.player_.on('network-info', (_event, data: PlayerNetworkInfo) => {
89 // We are in HTTP fallback 88 if (data.p2p) return
89
90 if (data.source === 'web-video') subDivHttpText.textContent = 'HTTP'
91 else if (data.source === 'p2p-media-loader') subDivHttpText.textContent = 'HLS'
92
93 // We are in HTTP mode
90 subDivHttp.className = 'vjs-peertube-displayed' 94 subDivHttp.className = 'vjs-peertube-displayed'
91 subDivP2P.className = 'vjs-peertube-hidden' 95 subDivP2P.className = 'vjs-peertube-hidden'
92 96
diff --git a/client/src/assets/player/shared/metrics/metrics-plugin.ts b/client/src/assets/player/shared/metrics/metrics-plugin.ts
index 20d37d636..06ca0c2f2 100644
--- a/client/src/assets/player/shared/metrics/metrics-plugin.ts
+++ b/client/src/assets/player/shared/metrics/metrics-plugin.ts
@@ -19,7 +19,7 @@ class MetricsPlugin extends Plugin {
19 private errors = 0 19 private errors = 0
20 20
21 private p2pEnabled: boolean 21 private p2pEnabled: boolean
22 private totalPeers = 0 22 private p2pPeers = 0
23 23
24 private lastPlayerNetworkInfo: PlayerNetworkInfo 24 private lastPlayerNetworkInfo: PlayerNetworkInfo
25 25
@@ -111,12 +111,12 @@ class MetricsPlugin extends Plugin {
111 111
112 errors: this.errors, 112 errors: this.errors,
113 113
114 downloadedBytesP2P: this.downloadedBytesP2P,
115 downloadedBytesHTTP: this.downloadedBytesHTTP, 114 downloadedBytesHTTP: this.downloadedBytesHTTP,
116 115
116 downloadedBytesP2P: this.downloadedBytesP2P,
117 uploadedBytesP2P: this.uploadedBytesP2P, 117 uploadedBytesP2P: this.uploadedBytesP2P,
118 118
119 totalPeers: this.totalPeers, 119 p2pPeers: this.p2pPeers,
120 p2pEnabled: this.p2pEnabled, 120 p2pEnabled: this.p2pEnabled,
121 121
122 videoId: this.options_.videoUUID() 122 videoId: this.options_.videoUUID()
@@ -139,23 +139,14 @@ class MetricsPlugin extends Plugin {
139 } 139 }
140 140
141 private trackBytes () { 141 private trackBytes () {
142 this.player.on('p2p-info', (_event, data: PlayerNetworkInfo) => { 142 this.player.on('network-info', (_event, data: PlayerNetworkInfo) => {
143 this.downloadedBytesHTTP += Math.round(data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0)) 143 this.downloadedBytesHTTP += Math.round(data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0))
144 this.downloadedBytesP2P += Math.round(data.p2p.downloaded - (this.lastPlayerNetworkInfo?.p2p.downloaded || 0)) 144 this.downloadedBytesP2P += Math.round((data.p2p?.downloaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.downloaded || 0))
145
146 this.uploadedBytesP2P += Math.round(data.p2p.uploaded - (this.lastPlayerNetworkInfo?.p2p.uploaded || 0))
147
148 this.totalPeers = data.p2p.numPeers
149 this.p2pEnabled = true
150 145
151 this.lastPlayerNetworkInfo = data 146 this.uploadedBytesP2P += Math.round((data.p2p?.uploaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.uploaded || 0))
152 })
153
154 this.player.on('http-info', (_event, data: PlayerNetworkInfo) => {
155 this.downloadedBytesHTTP += Math.round(data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0))
156 147
157 this.totalPeers = 0 148 this.p2pPeers = data.p2p?.peersP2POnly
158 this.p2pEnabled = false 149 this.p2pEnabled = !!data.p2p
159 150
160 this.lastPlayerNetworkInfo = data 151 this.lastPlayerNetworkInfo = data
161 }) 152 })
diff --git a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
index fe967a730..8c376cd21 100644
--- a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
+++ b/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
@@ -16,7 +16,8 @@ class P2pMediaLoaderPlugin extends Plugin {
16 private statsP2PBytes = { 16 private statsP2PBytes = {
17 pendingDownload: [] as number[], 17 pendingDownload: [] as number[],
18 pendingUpload: [] as number[], 18 pendingUpload: [] as number[],
19 numPeers: 0, 19 peersWithWebSeed: 0,
20 peersP2POnly: 0,
20 totalDownload: 0, 21 totalDownload: 0,
21 totalUpload: 0 22 totalUpload: 0
22 } 23 }
@@ -113,7 +114,7 @@ class P2pMediaLoaderPlugin extends Plugin {
113 this.options.redundancyUrlManager.removeBySegmentUrl(segment.requestUrl) 114 this.options.redundancyUrlManager.removeBySegmentUrl(segment.requestUrl)
114 }) 115 })
115 116
116 this.statsP2PBytes.numPeers = 1 + this.options.redundancyUrlManager.countBaseUrls() 117 this.statsP2PBytes.peersWithWebSeed = 1 + this.options.redundancyUrlManager.countBaseUrls()
117 118
118 this.runStats() 119 this.runStats()
119 120
@@ -138,8 +139,14 @@ class P2pMediaLoaderPlugin extends Plugin {
138 this.statsP2PBytes.totalUpload += bytes 139 this.statsP2PBytes.totalUpload += bytes
139 }) 140 })
140 141
141 this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++) 142 this.p2pEngine.on(Events.PeerConnect, () => {
142 this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--) 143 this.statsP2PBytes.peersWithWebSeed++
144 this.statsP2PBytes.peersP2POnly++
145 })
146 this.p2pEngine.on(Events.PeerClose, () => {
147 this.statsP2PBytes.peersWithWebSeed--
148 this.statsP2PBytes.peersP2POnly--
149 })
143 150
144 this.networkInfoInterval = setInterval(() => { 151 this.networkInfoInterval = setInterval(() => {
145 const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload) 152 const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload)
@@ -151,20 +158,23 @@ class P2pMediaLoaderPlugin extends Plugin {
151 this.statsP2PBytes.pendingUpload = [] 158 this.statsP2PBytes.pendingUpload = []
152 this.statsHTTPBytes.pendingDownload = [] 159 this.statsHTTPBytes.pendingDownload = []
153 160
154 return this.player.trigger('p2p-info', { 161 return this.player.trigger('network-info', {
155 source: 'p2p-media-loader', 162 source: 'p2p-media-loader',
163 bandwidthEstimate: (this.hlsjs as any).bandwidthEstimate / 8,
156 http: { 164 http: {
157 downloadSpeed: httpDownloadSpeed, 165 downloadSpeed: httpDownloadSpeed,
158 downloaded: this.statsHTTPBytes.totalDownload 166 downloaded: this.statsHTTPBytes.totalDownload
159 }, 167 },
160 p2p: { 168 p2p: this.options.p2pEnabled
161 downloadSpeed: p2pDownloadSpeed, 169 ? {
162 uploadSpeed: p2pUploadSpeed, 170 downloadSpeed: p2pDownloadSpeed,
163 numPeers: this.statsP2PBytes.numPeers, 171 uploadSpeed: p2pUploadSpeed,
164 downloaded: this.statsP2PBytes.totalDownload, 172 peersWithWebSeed: this.statsP2PBytes.peersWithWebSeed,
165 uploaded: this.statsP2PBytes.totalUpload 173 peersP2POnly: this.statsP2PBytes.peersP2POnly,
166 }, 174 downloaded: this.statsP2PBytes.totalDownload,
167 bandwidthEstimate: (this.hlsjs as any).bandwidthEstimate / 8 175 uploaded: this.statsP2PBytes.totalUpload
176 }
177 : undefined
168 } as PlayerNetworkInfo) 178 } as PlayerNetworkInfo)
169 }, 1000) 179 }, 1000)
170 } 180 }
diff --git a/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts b/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts
index 1658f776c..fd632d90d 100644
--- a/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts
+++ b/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts
@@ -44,6 +44,8 @@ export class HLSOptionsBuilder {
44 requiresUserAuth: this.options.requiresUserAuth, 44 requiresUserAuth: this.options.requiresUserAuth,
45 videoFileToken: this.options.videoFileToken, 45 videoFileToken: this.options.videoFileToken,
46 46
47 p2pEnabled: this.options.p2pEnabled,
48
47 redundancyUrlManager, 49 redundancyUrlManager,
48 type: 'application/x-mpegURL', 50 type: 'application/x-mpegURL',
49 src: this.options.hls.playlistUrl, 51 src: this.options.hls.playlistUrl,
diff --git a/client/src/assets/player/shared/stats/stats-card.ts b/client/src/assets/player/shared/stats/stats-card.ts
index 077c900e5..13334d91a 100644
--- a/client/src/assets/player/shared/stats/stats-card.ts
+++ b/client/src/assets/player/shared/stats/stats-card.ts
@@ -63,8 +63,7 @@ class StatsCard extends Component {
63 63
64 private liveLatency: InfoElement 64 private liveLatency: InfoElement
65 65
66 private onP2PInfoHandler: (_event: any, data: EventPlayerNetworkInfo) => void 66 private onNetworkInfoHandler: (_event: any, data: EventPlayerNetworkInfo) => void
67 private onHTTPInfoHandler: (_event: any, data: EventPlayerNetworkInfo) => void
68 67
69 createEl () { 68 createEl () {
70 this.containerEl = videojs.dom.createEl('div', { 69 this.containerEl = videojs.dom.createEl('div', {
@@ -89,33 +88,26 @@ class StatsCard extends Component {
89 88
90 this.populateInfoBlocks() 89 this.populateInfoBlocks()
91 90
92 this.onP2PInfoHandler = (_event, data) => { 91 this.onNetworkInfoHandler = (_event, data) => {
93 this.mode = data.source 92 this.mode = data.source
94 93
95 const p2pStats = data.p2p 94 const p2pStats = data.p2p
96 const httpStats = data.http 95 const httpStats = data.http
97 96
98 this.playerNetworkInfo.downloadSpeed = bytes(p2pStats.downloadSpeed + httpStats.downloadSpeed).join(' ') 97 this.playerNetworkInfo.downloadSpeed = bytes((p2pStats?.downloadSpeed || 0) + (httpStats.downloadSpeed || 0)).join(' ')
99 this.playerNetworkInfo.uploadSpeed = bytes(p2pStats.uploadSpeed).join(' ') 98 this.playerNetworkInfo.uploadSpeed = bytes(p2pStats?.uploadSpeed || 0).join(' ')
100 this.playerNetworkInfo.totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded).join(' ') 99 this.playerNetworkInfo.totalDownloaded = bytes((p2pStats?.downloaded || 0) + httpStats.downloaded).join(' ')
101 this.playerNetworkInfo.totalUploaded = bytes(p2pStats.uploaded).join(' ') 100 this.playerNetworkInfo.totalUploaded = bytes(p2pStats?.uploaded || 0).join(' ')
102 this.playerNetworkInfo.numPeers = p2pStats.numPeers 101 this.playerNetworkInfo.numPeers = p2pStats?.peersWithWebSeed
103 this.playerNetworkInfo.averageBandwidth = bytes(data.bandwidthEstimate).join(' ') + '/s'
104 102
105 if (data.source === 'p2p-media-loader') { 103 if (data.source === 'p2p-media-loader') {
104 this.playerNetworkInfo.averageBandwidth = bytes(data.bandwidthEstimate).join(' ') + '/s'
106 this.playerNetworkInfo.downloadedFromServer = bytes(httpStats.downloaded).join(' ') 105 this.playerNetworkInfo.downloadedFromServer = bytes(httpStats.downloaded).join(' ')
107 this.playerNetworkInfo.downloadedFromPeers = bytes(p2pStats.downloaded).join(' ') 106 this.playerNetworkInfo.downloadedFromPeers = bytes(p2pStats?.downloaded || 0).join(' ')
108 } 107 }
109 } 108 }
110 109
111 this.onHTTPInfoHandler = (_event, data) => { 110 this.player().on('network-info', this.onNetworkInfoHandler)
112 this.mode = data.source
113
114 this.playerNetworkInfo.totalDownloaded = bytes(data.http.downloaded).join(' ')
115 }
116
117 this.player().on('p2p-info', this.onP2PInfoHandler)
118 this.player().on('http-info', this.onHTTPInfoHandler)
119 111
120 return this.containerEl 112 return this.containerEl
121 } 113 }
@@ -123,8 +115,7 @@ class StatsCard extends Component {
123 dispose () { 115 dispose () {
124 if (this.updateInterval) clearInterval(this.updateInterval) 116 if (this.updateInterval) clearInterval(this.updateInterval)
125 117
126 this.player().off('p2p-info', this.onP2PInfoHandler) 118 this.player().off('network-info', this.onNetworkInfoHandler)
127 this.player().off('http-info', this.onHTTPInfoHandler)
128 119
129 super.dispose() 120 super.dispose()
130 } 121 }
diff --git a/client/src/assets/player/shared/web-video/web-video-plugin.ts b/client/src/assets/player/shared/web-video/web-video-plugin.ts
index 930b5045a..d09b5a724 100644
--- a/client/src/assets/player/shared/web-video/web-video-plugin.ts
+++ b/client/src/assets/player/shared/web-video/web-video-plugin.ts
@@ -175,7 +175,7 @@ class WebVideoPlugin extends Plugin {
175 175
176 private setupNetworkInfoInterval () { 176 private setupNetworkInfoInterval () {
177 this.networkInfoInterval = setInterval(() => { 177 this.networkInfoInterval = setInterval(() => {
178 return this.player.trigger('http-info', { 178 return this.player.trigger('network-info', {
179 source: 'web-video', 179 source: 'web-video',
180 http: { 180 http: {
181 downloaded: this.player.bufferedPercent() * this.currentVideoFile.size 181 downloaded: this.player.bufferedPercent() * this.currentVideoFile.size
diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/assets/player/types/peertube-videojs-typings.ts
index adaa43d77..73b38d0d3 100644
--- a/client/src/assets/player/types/peertube-videojs-typings.ts
+++ b/client/src/assets/player/types/peertube-videojs-typings.ts
@@ -184,6 +184,8 @@ type P2PMediaLoaderPluginOptions = {
184 type: string 184 type: string
185 src: string 185 src: string
186 186
187 p2pEnabled: boolean
188
187 loader: P2PMediaLoader 189 loader: P2PMediaLoader
188 segmentValidator: SegmentValidator 190 segmentValidator: SegmentValidator
189 191
@@ -240,9 +242,12 @@ type PlayerNetworkInfo = {
240 p2p?: { 242 p2p?: {
241 downloadSpeed: number 243 downloadSpeed: number
242 uploadSpeed: number 244 uploadSpeed: number
245
243 downloaded: number 246 downloaded: number
244 uploaded: number 247 uploaded: number
245 numPeers: number 248
249 peersWithWebSeed: number
250 peersP2POnly: number
246 } 251 }
247 252
248 // In bytes 253 // In bytes
diff --git a/config/dev.yaml b/config/dev.yaml
index f570ede1e..60179533d 100644
--- a/config/dev.yaml
+++ b/config/dev.yaml
@@ -35,6 +35,10 @@ smtp:
35log: 35log:
36 level: 'debug' 36 level: 'debug'
37 37
38open_telemetry:
39 metrics:
40 enabled: true
41
38contact_form: 42contact_form:
39 enabled: true 43 enabled: true
40 44
diff --git a/server/lib/opentelemetry/metric-helpers/playback-metrics.ts b/server/lib/opentelemetry/metric-helpers/playback-metrics.ts
index 41a5dd640..1eb08b5a6 100644
--- a/server/lib/opentelemetry/metric-helpers/playback-metrics.ts
+++ b/server/lib/opentelemetry/metric-helpers/playback-metrics.ts
@@ -1,4 +1,4 @@
1import { Counter, Histogram, Meter } from '@opentelemetry/api' 1import { Counter, Meter } from '@opentelemetry/api'
2import { MVideoImmutable } from '@server/types/models' 2import { MVideoImmutable } from '@server/types/models'
3import { PlaybackMetricCreate } from '@shared/models' 3import { PlaybackMetricCreate } from '@shared/models'
4 4
@@ -11,7 +11,10 @@ export class PlaybackMetrics {
11 11
12 private downloadedBytesHTTPCounter: Counter 12 private downloadedBytesHTTPCounter: Counter
13 13
14 private peersP2PPeers: Histogram 14 private peersP2PPeersGaugeBuffer: {
15 value: number
16 attributes: any
17 }[] = []
15 18
16 constructor (private readonly meter: Meter) { 19 constructor (private readonly meter: Meter) {
17 20
@@ -37,8 +40,14 @@ export class PlaybackMetrics {
37 description: 'Uploaded bytes with P2P by PeerTube player.' 40 description: 'Uploaded bytes with P2P by PeerTube player.'
38 }) 41 })
39 42
40 this.peersP2PPeers = this.meter.createHistogram('peertube_playback_p2p_peers', { 43 this.meter.createObservableGauge('peertube_playback_p2p_peers', {
41 description: 'Total P2P peers connected to the PeerTube player.' 44 description: 'Total P2P peers connected to the PeerTube player.'
45 }).addCallback(observableResult => {
46 for (const gauge of this.peersP2PPeersGaugeBuffer) {
47 observableResult.observe(gauge.value, gauge.attributes)
48 }
49
50 this.peersP2PPeersGaugeBuffer = []
42 }) 51 })
43 } 52 }
44 53
@@ -66,6 +75,11 @@ export class PlaybackMetrics {
66 75
67 this.uploadedBytesP2PCounter.add(metrics.uploadedBytesP2P, attributes) 76 this.uploadedBytesP2PCounter.add(metrics.uploadedBytesP2P, attributes)
68 77
69 if (metrics.totalPeers) this.peersP2PPeers.record(metrics.totalPeers, attributes) 78 if (metrics.p2pPeers) {
79 this.peersP2PPeersGaugeBuffer.push({
80 value: metrics.p2pPeers,
81 attributes
82 })
83 }
70 } 84 }
71} 85}
diff --git a/server/middlewares/validators/metrics.ts b/server/middlewares/validators/metrics.ts
index ced9afc69..986b30a19 100644
--- a/server/middlewares/validators/metrics.ts
+++ b/server/middlewares/validators/metrics.ts
@@ -13,12 +13,11 @@ const addPlaybackMetricValidator = [
13 .optional() 13 .optional()
14 .isInt({ min: 0 }), 14 .isInt({ min: 0 }),
15 15
16 body('totalPeers') 16 body('p2pPeers')
17 .optional() 17 .optional()
18 .isInt({ min: 0 }), 18 .isInt({ min: 0 }),
19 19
20 body('p2pEnabled') 20 body('p2pEnabled')
21 .optional()
22 .isBoolean(), 21 .isBoolean(),
23 22
24 body('playerMode') 23 body('playerMode')
diff --git a/server/tests/api/check-params/metrics.ts b/server/tests/api/check-params/metrics.ts
index 25443401d..302bef4f5 100644
--- a/server/tests/api/check-params/metrics.ts
+++ b/server/tests/api/check-params/metrics.ts
@@ -38,6 +38,7 @@ describe('Test metrics API validators', function () {
38 fps: 30, 38 fps: 30,
39 resolutionChanges: 1, 39 resolutionChanges: 1,
40 errors: 2, 40 errors: 2,
41 p2pEnabled: true,
41 downloadedBytesP2P: 0, 42 downloadedBytesP2P: 0,
42 downloadedBytesHTTP: 0, 43 downloadedBytesHTTP: 0,
43 uploadedBytesP2P: 0, 44 uploadedBytesP2P: 0,
@@ -145,7 +146,13 @@ describe('Test metrics API validators', function () {
145 }) 146 })
146 }) 147 })
147 148
148 it('Should fail with an invalid p2pEnabled', async function () { 149 it('Should fail with a missing/invalid p2pEnabled', async function () {
150 await makePostBodyRequest({
151 url: server.url,
152 path,
153 fields: omit(baseParams, [ 'p2pEnabled' ])
154 })
155
149 await makePostBodyRequest({ 156 await makePostBodyRequest({
150 url: server.url, 157 url: server.url,
151 path, 158 path,
@@ -157,7 +164,7 @@ describe('Test metrics API validators', function () {
157 await makePostBodyRequest({ 164 await makePostBodyRequest({
158 url: server.url, 165 url: server.url,
159 path, 166 path,
160 fields: { ...baseParams, totalPeers: 'toto' } 167 fields: { ...baseParams, p2pPeers: 'toto' }
161 }) 168 })
162 }) 169 })
163 170
diff --git a/server/tests/api/server/open-telemetry.ts b/server/tests/api/server/open-telemetry.ts
index 0bd0b5e86..508e9d649 100644
--- a/server/tests/api/server/open-telemetry.ts
+++ b/server/tests/api/server/open-telemetry.ts
@@ -2,7 +2,7 @@
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { expectLogContain, expectLogDoesNotContain, MockHTTP } from '@server/tests/shared' 4import { expectLogContain, expectLogDoesNotContain, MockHTTP } from '@server/tests/shared'
5import { HttpStatusCode, VideoPrivacy, VideoResolution } from '@shared/models' 5import { HttpStatusCode, PlaybackMetricCreate, VideoPrivacy, VideoResolution } from '@shared/models'
6import { cleanupTests, createSingleServer, makeRawRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' 6import { cleanupTests, createSingleServer, makeRawRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
7 7
8describe('Open Telemetry', function () { 8describe('Open Telemetry', function () {
@@ -62,14 +62,49 @@ describe('Open Telemetry', function () {
62 downloadedBytesP2P: 0, 62 downloadedBytesP2P: 0,
63 downloadedBytesHTTP: 0, 63 downloadedBytesHTTP: 0,
64 uploadedBytesP2P: 5, 64 uploadedBytesP2P: 5,
65 totalPeers: 1, 65 p2pPeers: 1,
66 p2pEnabled: false, 66 p2pEnabled: false,
67 videoId: video.uuid 67 videoId: video.uuid
68 } 68 }
69 }) 69 })
70 70
71 const res = await makeRawRequest({ url: metricsUrl, expectedStatus: HttpStatusCode.OK_200 }) 71 const res = await makeRawRequest({ url: metricsUrl, expectedStatus: HttpStatusCode.OK_200 })
72
72 expect(res.text).to.contain('peertube_playback_http_downloaded_bytes_total{') 73 expect(res.text).to.contain('peertube_playback_http_downloaded_bytes_total{')
74 expect(res.text).to.contain('peertube_playback_p2p_peers{')
75 expect(res.text).to.contain('p2pEnabled="false"')
76 })
77
78 it('Should take the last playback metric', async function () {
79 await setAccessTokensToServers([ server ])
80
81 const video = await server.videos.quickUpload({ name: 'video' })
82
83 const metrics = {
84 playerMode: 'p2p-media-loader',
85 resolution: VideoResolution.H_1080P,
86 fps: 30,
87 resolutionChanges: 1,
88 errors: 2,
89 downloadedBytesP2P: 0,
90 downloadedBytesHTTP: 0,
91 uploadedBytesP2P: 5,
92 p2pPeers: 7,
93 p2pEnabled: false,
94 videoId: video.uuid
95 } as PlaybackMetricCreate
96
97 await server.metrics.addPlaybackMetric({ metrics })
98
99 metrics.p2pPeers = 42
100 await server.metrics.addPlaybackMetric({ metrics })
101
102 const res = await makeRawRequest({ url: metricsUrl, expectedStatus: HttpStatusCode.OK_200 })
103
104 // eslint-disable-next-line max-len
105 const label = `{videoOrigin="local",playerMode="p2p-media-loader",resolution="1080",fps="30",p2pEnabled="false",videoUUID="${video.uuid}"}`
106 expect(res.text).to.contain(`peertube_playback_p2p_peers${label} 42`)
107 expect(res.text).to.not.contain(`peertube_playback_p2p_peers${label} 7`)
73 }) 108 })
74 109
75 it('Should disable http request duration metrics', async function () { 110 it('Should disable http request duration metrics', async function () {
diff --git a/shared/models/metrics/playback-metric-create.model.ts b/shared/models/metrics/playback-metric-create.model.ts
index b428beeb6..1d47421c3 100644
--- a/shared/models/metrics/playback-metric-create.model.ts
+++ b/shared/models/metrics/playback-metric-create.model.ts
@@ -6,8 +6,8 @@ export interface PlaybackMetricCreate {
6 resolution?: VideoResolution 6 resolution?: VideoResolution
7 fps?: number 7 fps?: number
8 8
9 p2pEnabled?: boolean 9 p2pEnabled: boolean
10 totalPeers?: number 10 p2pPeers?: number
11 11
12 resolutionChanges: number 12 resolutionChanges: number
13 13
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 2dfad9987..90aaebd26 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -9528,6 +9528,11 @@ components:
9528 fps: 9528 fps:
9529 type: number 9529 type: number
9530 description: Current player video fps 9530 description: Current player video fps
9531 p2pEnabled:
9532 type: boolean
9533 p2pPeers:
9534 type: number
9535 description: P2P peers connected (doesn't include WebSeed peers)
9531 resolutionChanges: 9536 resolutionChanges:
9532 type: number 9537 type: number
9533 description: How many resolution changes occured since the last metric creation 9538 description: How many resolution changes occured since the last metric creation
@@ -9555,6 +9560,7 @@ components:
9555 - downloadedBytesP2P 9560 - downloadedBytesP2P
9556 - downloadedBytesHTTP 9561 - downloadedBytesHTTP
9557 - uploadedBytesP2P 9562 - uploadedBytesP2P
9563 - p2pEnabled
9558 - videoId 9564 - videoId
9559 9565
9560 RunnerRegistrationToken: 9566 RunnerRegistrationToken: