metrics.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { Application, Request, Response } from 'express'
  2. import { Meter, metrics } from '@opentelemetry/api'
  3. import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'
  4. import { MeterProvider } from '@opentelemetry/sdk-metrics'
  5. import { logger } from '@server/helpers/logger.js'
  6. import { CONFIG } from '@server/initializers/config.js'
  7. import { MVideoImmutable } from '@server/types/models/index.js'
  8. import { PlaybackMetricCreate } from '@peertube/peertube-models'
  9. import {
  10. BittorrentTrackerObserversBuilder,
  11. JobQueueObserversBuilder,
  12. LivesObserversBuilder,
  13. NodeJSObserversBuilder,
  14. PlaybackMetrics,
  15. StatsObserversBuilder,
  16. ViewersObserversBuilder
  17. } from './metric-helpers/index.js'
  18. import { WorkerThreadsObserversBuilder } from './metric-helpers/worker-threads-observers.js'
  19. class OpenTelemetryMetrics {
  20. private static instance: OpenTelemetryMetrics
  21. private meter: Meter
  22. private onRequestDuration: (req: Request, res: Response) => void
  23. private playbackMetrics: PlaybackMetrics
  24. private constructor () {}
  25. init (app: Application) {
  26. if (CONFIG.OPEN_TELEMETRY.METRICS.ENABLED !== true) return
  27. app.use((req, res, next) => {
  28. res.once('finish', () => {
  29. if (!this.onRequestDuration) return
  30. this.onRequestDuration(req as Request, res as Response)
  31. })
  32. next()
  33. })
  34. }
  35. registerMetrics (options: { trackerServer: any }) {
  36. if (CONFIG.OPEN_TELEMETRY.METRICS.ENABLED !== true) return
  37. logger.info('Registering Open Telemetry metrics')
  38. const provider = new MeterProvider({
  39. views: [
  40. ...NodeJSObserversBuilder.getViews()
  41. ],
  42. readers: [
  43. new PrometheusExporter({
  44. host: CONFIG.OPEN_TELEMETRY.METRICS.PROMETHEUS_EXPORTER.HOSTNAME,
  45. port: CONFIG.OPEN_TELEMETRY.METRICS.PROMETHEUS_EXPORTER.PORT
  46. })
  47. ]
  48. })
  49. metrics.setGlobalMeterProvider(provider)
  50. this.meter = metrics.getMeter('default')
  51. if (CONFIG.OPEN_TELEMETRY.METRICS.HTTP_REQUEST_DURATION.ENABLED === true) {
  52. this.buildRequestObserver()
  53. }
  54. this.playbackMetrics = new PlaybackMetrics(this.meter)
  55. this.playbackMetrics.buildCounters()
  56. new NodeJSObserversBuilder(this.meter).buildObservers()
  57. new JobQueueObserversBuilder(this.meter).buildObservers()
  58. new StatsObserversBuilder(this.meter).buildObservers()
  59. new LivesObserversBuilder(this.meter).buildObservers()
  60. new ViewersObserversBuilder(this.meter).buildObservers()
  61. new WorkerThreadsObserversBuilder(this.meter).buildObservers()
  62. new BittorrentTrackerObserversBuilder(this.meter, options.trackerServer).buildObservers()
  63. }
  64. observePlaybackMetric (video: MVideoImmutable, metrics: PlaybackMetricCreate) {
  65. this.playbackMetrics.observe(video, metrics)
  66. }
  67. private buildRequestObserver () {
  68. const requestDuration = this.meter.createHistogram('http_request_duration_ms', {
  69. unit: 'milliseconds',
  70. description: 'Duration of HTTP requests in ms'
  71. })
  72. this.onRequestDuration = (req: Request, res: Response) => {
  73. const duration = Date.now() - res.locals.requestStart
  74. requestDuration.record(duration, {
  75. path: this.buildRequestPath(req.originalUrl),
  76. method: req.method,
  77. statusCode: res.statusCode + ''
  78. })
  79. }
  80. }
  81. private buildRequestPath (path: string) {
  82. return path.split('?')[0]
  83. }
  84. static get Instance () {
  85. return this.instance || (this.instance = new this())
  86. }
  87. }
  88. export {
  89. OpenTelemetryMetrics
  90. }