Add live and viewers otel metrics
This commit is contained in:
parent
50cc1ee48a
commit
adc94cf09c
8 changed files with 106 additions and 14 deletions
|
@ -1,3 +1,5 @@
|
|||
export * from './lives-observers-builder'
|
||||
export * from './job-queue-observers-builder'
|
||||
export * from './nodejs-observers-builder'
|
||||
export * from './stats-observers-builder'
|
||||
export * from './viewers-observers-builder'
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { Meter } from '@opentelemetry/api-metrics'
|
||||
import { VideoModel } from '@server/models/video/video'
|
||||
|
||||
export class LivesObserversBuilder {
|
||||
|
||||
constructor (private readonly meter: Meter) {
|
||||
|
||||
}
|
||||
|
||||
buildObservers () {
|
||||
this.meter.createObservableGauge('peertube_running_lives_total', {
|
||||
description: 'Total running lives on the instance'
|
||||
}).addCallback(async observableResult => {
|
||||
const local = await VideoModel.countLives({ remote: false, mode: 'published' })
|
||||
const remote = await VideoModel.countLives({ remote: true, mode: 'published' })
|
||||
|
||||
observableResult.observe(local, { liveOrigin: 'local' })
|
||||
observableResult.observe(remote, { liveOrigin: 'remote' })
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { Meter } from '@opentelemetry/api-metrics'
|
||||
import { VideoScope, ViewerScope } from '@server/lib/views/shared'
|
||||
import { VideoViewsManager } from '@server/lib/views/video-views-manager'
|
||||
|
||||
export class ViewersObserversBuilder {
|
||||
|
||||
constructor (private readonly meter: Meter) {
|
||||
|
||||
}
|
||||
|
||||
buildObservers () {
|
||||
this.meter.createObservableGauge('peertube_viewers_total', {
|
||||
description: 'Total viewers on the instance'
|
||||
}).addCallback(observableResult => {
|
||||
for (const viewerScope of [ 'local', 'remote' ] as ViewerScope[]) {
|
||||
for (const videoScope of [ 'local', 'remote' ] as VideoScope[]) {
|
||||
const result = VideoViewsManager.Instance.getTotalViewers({ viewerScope, videoScope })
|
||||
|
||||
observableResult.observe(result, { viewerOrigin: viewerScope, videoOrigin: videoScope })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -4,7 +4,13 @@ import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'
|
|||
import { MeterProvider } from '@opentelemetry/sdk-metrics-base'
|
||||
import { logger } from '@server/helpers/logger'
|
||||
import { CONFIG } from '@server/initializers/config'
|
||||
import { JobQueueObserversBuilder, NodeJSObserversBuilder, StatsObserversBuilder } from './metric-helpers'
|
||||
import {
|
||||
JobQueueObserversBuilder,
|
||||
LivesObserversBuilder,
|
||||
NodeJSObserversBuilder,
|
||||
StatsObserversBuilder,
|
||||
ViewersObserversBuilder
|
||||
} from './metric-helpers'
|
||||
|
||||
class OpenTelemetryMetrics {
|
||||
|
||||
|
@ -53,6 +59,12 @@ class OpenTelemetryMetrics {
|
|||
|
||||
const statsObserversBuilder = new StatsObserversBuilder(this.meter)
|
||||
statsObserversBuilder.buildObservers()
|
||||
|
||||
const livesObserversBuilder = new LivesObserversBuilder(this.meter)
|
||||
livesObserversBuilder.buildObservers()
|
||||
|
||||
const viewersObserversBuilder = new ViewersObserversBuilder(this.meter)
|
||||
viewersObserversBuilder.buildObservers()
|
||||
}
|
||||
|
||||
private buildRequestObserver () {
|
||||
|
|
|
@ -10,9 +10,14 @@ import { buildUUID, sha256 } from '@shared/extra-utils'
|
|||
|
||||
const lTags = loggerTagsFactory('views')
|
||||
|
||||
export type ViewerScope = 'local' | 'remote'
|
||||
export type VideoScope = 'local' | 'remote'
|
||||
|
||||
type Viewer = {
|
||||
expires: number
|
||||
id: string
|
||||
viewerScope: ViewerScope
|
||||
videoScope: VideoScope
|
||||
lastFederation?: number
|
||||
}
|
||||
|
||||
|
@ -50,7 +55,7 @@ export class VideoViewerCounters {
|
|||
return false
|
||||
}
|
||||
|
||||
const newViewer = await this.addViewerToVideo({ viewerId, video })
|
||||
const newViewer = await this.addViewerToVideo({ viewerId, video, viewerScope: 'local' })
|
||||
await this.federateViewerIfNeeded(video, newViewer)
|
||||
|
||||
return true
|
||||
|
@ -65,13 +70,26 @@ export class VideoViewerCounters {
|
|||
|
||||
logger.debug('Adding remote viewer to video %s.', video.uuid, { ...lTags(video.uuid) })
|
||||
|
||||
await this.addViewerToVideo({ video, viewerExpires, viewerId })
|
||||
await this.addViewerToVideo({ video, viewerExpires, viewerId, viewerScope: 'remote' })
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getTotalViewers (options: {
|
||||
viewerScope: ViewerScope
|
||||
videoScope: VideoScope
|
||||
}) {
|
||||
let total = 0
|
||||
|
||||
for (const viewers of this.viewersPerVideo.values()) {
|
||||
total += viewers.filter(v => v.viewerScope === options.viewerScope && v.videoScope === options.videoScope).length
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
getViewers (video: MVideo) {
|
||||
const viewers = this.viewersPerVideo.get(video.id)
|
||||
if (!viewers) return 0
|
||||
|
@ -88,9 +106,10 @@ export class VideoViewerCounters {
|
|||
private async addViewerToVideo (options: {
|
||||
video: MVideoImmutable
|
||||
viewerId: string
|
||||
viewerScope: ViewerScope
|
||||
viewerExpires?: Date
|
||||
}) {
|
||||
const { video, viewerExpires, viewerId } = options
|
||||
const { video, viewerExpires, viewerId, viewerScope } = options
|
||||
|
||||
let watchers = this.viewersPerVideo.get(video.id)
|
||||
|
||||
|
@ -103,7 +122,11 @@ export class VideoViewerCounters {
|
|||
? viewerExpires.getTime()
|
||||
: this.buildViewerExpireTime()
|
||||
|
||||
const viewer = { id: viewerId, expires }
|
||||
const videoScope: VideoScope = video.remote
|
||||
? 'remote'
|
||||
: 'local'
|
||||
|
||||
const viewer = { id: viewerId, expires, videoScope, viewerScope }
|
||||
watchers.push(viewer)
|
||||
|
||||
this.idToViewer.set(viewerId, viewer)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||
import { MVideo, MVideoImmutable } from '@server/types/models'
|
||||
import { VideoViewEvent } from '@shared/models'
|
||||
import { VideoViewerCounters, VideoViewerStats, VideoViews } from './shared'
|
||||
import { VideoScope, VideoViewerCounters, VideoViewerStats, VideoViews, ViewerScope } from './shared'
|
||||
|
||||
/**
|
||||
* If processing a local view:
|
||||
|
@ -79,6 +79,13 @@ export class VideoViewsManager {
|
|||
return this.videoViewerCounters.getViewers(video)
|
||||
}
|
||||
|
||||
getTotalViewers (options: {
|
||||
viewerScope: ViewerScope
|
||||
videoScope: VideoScope
|
||||
}) {
|
||||
return this.videoViewerCounters.getTotalViewers(options)
|
||||
}
|
||||
|
||||
buildViewerExpireTime () {
|
||||
return this.videoViewerCounters.buildViewerExpireTime()
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([
|
|||
if (!await doesVideoChannelOfAccountExist(body.channelId, user, res)) return cleanUpReqFiles(req)
|
||||
|
||||
if (CONFIG.LIVE.MAX_INSTANCE_LIVES !== -1) {
|
||||
const totalInstanceLives = await VideoModel.countLocalLives()
|
||||
const totalInstanceLives = await VideoModel.countLives({ remote: false, mode: 'not-ended' })
|
||||
|
||||
if (totalInstanceLives >= CONFIG.LIVE.MAX_INSTANCE_LIVES) {
|
||||
cleanUpReqFiles(req)
|
||||
|
|
|
@ -1209,18 +1209,21 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
return VideoModel.getAvailableForApi(queryOptions)
|
||||
}
|
||||
|
||||
static countLocalLives () {
|
||||
const options = {
|
||||
static countLives (options: {
|
||||
remote: boolean
|
||||
mode: 'published' | 'not-ended'
|
||||
}) {
|
||||
const query = {
|
||||
where: {
|
||||
remote: false,
|
||||
remote: options.remote,
|
||||
isLive: true,
|
||||
state: {
|
||||
[Op.ne]: VideoState.LIVE_ENDED
|
||||
}
|
||||
state: options.mode === 'not-ended'
|
||||
? { [Op.ne]: VideoState.LIVE_ENDED }
|
||||
: { [Op.eq]: VideoState.PUBLISHED }
|
||||
}
|
||||
}
|
||||
|
||||
return VideoModel.count(options)
|
||||
return VideoModel.count(query)
|
||||
}
|
||||
|
||||
static countVideosUploadedByUserSince (userId: number, since: Date) {
|
||||
|
|
Loading…
Reference in a new issue