metrics.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // @ts-check
  2. import metrics from 'prom-client';
  3. /**
  4. * @typedef StreamingMetrics
  5. * @property {metrics.Gauge<"type">} connectedClients
  6. * @property {metrics.Gauge<"type" | "channel">} connectedChannels
  7. * @property {metrics.Gauge} redisSubscriptions
  8. * @property {metrics.Counter} redisMessagesReceived
  9. * @property {metrics.Counter<"type">} messagesSent
  10. * @property {import('express').RequestHandler<{}>} requestHandler
  11. */
  12. /**
  13. *
  14. * @param {string[]} channels
  15. * @param {import('pg').Pool} pgPool
  16. * @returns {StreamingMetrics}
  17. */
  18. export function setupMetrics(channels, pgPool) {
  19. // Collect metrics from Node.js
  20. metrics.collectDefaultMetrics();
  21. new metrics.Gauge({
  22. name: 'pg_pool_total_connections',
  23. help: 'The total number of clients existing within the pool',
  24. collect() {
  25. this.set(pgPool.totalCount);
  26. },
  27. });
  28. new metrics.Gauge({
  29. name: 'pg_pool_idle_connections',
  30. help: 'The number of clients which are not checked out but are currently idle in the pool',
  31. collect() {
  32. this.set(pgPool.idleCount);
  33. },
  34. });
  35. new metrics.Gauge({
  36. name: 'pg_pool_waiting_queries',
  37. help: 'The number of queued requests waiting on a client when all clients are checked out',
  38. collect() {
  39. this.set(pgPool.waitingCount);
  40. },
  41. });
  42. const connectedClients = new metrics.Gauge({
  43. name: 'connected_clients',
  44. help: 'The number of clients connected to the streaming server',
  45. labelNames: ['type'],
  46. });
  47. const connectedChannels = new metrics.Gauge({
  48. name: 'connected_channels',
  49. help: 'The number of channels the streaming server is streaming to',
  50. labelNames: [ 'type', 'channel' ]
  51. });
  52. const redisSubscriptions = new metrics.Gauge({
  53. name: 'redis_subscriptions',
  54. help: 'The number of Redis channels the streaming server is subscribed to',
  55. });
  56. const redisMessagesReceived = new metrics.Counter({
  57. name: 'redis_messages_received_total',
  58. help: 'The total number of messages the streaming server has received from redis subscriptions'
  59. });
  60. const messagesSent = new metrics.Counter({
  61. name: 'messages_sent_total',
  62. help: 'The total number of messages the streaming server sent to clients per connection type',
  63. labelNames: [ 'type' ]
  64. });
  65. // Prime the gauges so we don't loose metrics between restarts:
  66. redisSubscriptions.set(0);
  67. connectedClients.set({ type: 'websocket' }, 0);
  68. connectedClients.set({ type: 'eventsource' }, 0);
  69. // For each channel, initialize the gauges at zero; There's only a finite set of channels available
  70. channels.forEach(( channel ) => {
  71. connectedChannels.set({ type: 'websocket', channel }, 0);
  72. connectedChannels.set({ type: 'eventsource', channel }, 0);
  73. });
  74. // Prime the counters so that we don't loose metrics between restarts.
  75. // Unfortunately counters don't support the set() API, so instead I'm using
  76. // inc(0) to achieve the same result.
  77. redisMessagesReceived.inc(0);
  78. messagesSent.inc({ type: 'websocket' }, 0);
  79. messagesSent.inc({ type: 'eventsource' }, 0);
  80. /**
  81. * @type {import('express').RequestHandler<{}>}
  82. */
  83. const requestHandler = (req, res) => {
  84. metrics.register.metrics().then((output) => {
  85. res.set('Content-Type', metrics.register.contentType);
  86. res.set('Cache-Control', 'private, no-store');
  87. res.end(output);
  88. }).catch((err) => {
  89. req.log.error(err, "Error collecting metrics");
  90. res.set('Cache-Control', 'private, no-store');
  91. res.status(500).end();
  92. });
  93. };
  94. return {
  95. requestHandler,
  96. connectedClients,
  97. connectedChannels,
  98. redisSubscriptions,
  99. redisMessagesReceived,
  100. messagesSent,
  101. };
  102. }