EventLogger.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /* eslint-disable
  2. camelcase,
  3. */
  4. let EventLogger
  5. const logger = require('logger-sharelatex')
  6. const metrics = require('@overleaf/metrics')
  7. const settings = require('settings-sharelatex')
  8. // keep track of message counters to detect duplicate and out of order events
  9. // messsage ids have the format "UNIQUEHOSTKEY-COUNTER"
  10. const EVENT_LOG_COUNTER = {}
  11. const EVENT_LOG_TIMESTAMP = {}
  12. let EVENT_LAST_CLEAN_TIMESTAMP = 0
  13. // counter for debug logs
  14. let COUNTER = 0
  15. module.exports = EventLogger = {
  16. MAX_STALE_TIME_IN_MS: 3600 * 1000,
  17. debugEvent(channel, message) {
  18. if (settings.debugEvents > 0) {
  19. logger.log({ channel, message, counter: COUNTER++ }, 'logging event')
  20. settings.debugEvents--
  21. }
  22. },
  23. checkEventOrder(channel, message_id) {
  24. if (typeof message_id !== 'string') {
  25. return
  26. }
  27. let result
  28. if (!(result = message_id.match(/^(.*)-(\d+)$/))) {
  29. return
  30. }
  31. const key = result[1]
  32. const count = parseInt(result[2], 0)
  33. if (!(count >= 0)) {
  34. // ignore checks if counter is not present
  35. return
  36. }
  37. // store the last count in a hash for each host
  38. const previous = EventLogger._storeEventCount(key, count)
  39. if (!previous || count === previous + 1) {
  40. metrics.inc(`event.${channel}.valid`, 0.001) // downsample high rate docupdater events
  41. return // order is ok
  42. }
  43. if (count === previous) {
  44. metrics.inc(`event.${channel}.duplicate`)
  45. logger.warn({ channel, message_id }, 'duplicate event')
  46. return 'duplicate'
  47. } else {
  48. metrics.inc(`event.${channel}.out-of-order`)
  49. logger.warn(
  50. { channel, message_id, key, previous, count },
  51. 'out of order event'
  52. )
  53. return 'out-of-order'
  54. }
  55. },
  56. _storeEventCount(key, count) {
  57. const previous = EVENT_LOG_COUNTER[key]
  58. const now = Date.now()
  59. EVENT_LOG_COUNTER[key] = count
  60. EVENT_LOG_TIMESTAMP[key] = now
  61. // periodically remove old counts
  62. if (now - EVENT_LAST_CLEAN_TIMESTAMP > EventLogger.MAX_STALE_TIME_IN_MS) {
  63. EventLogger._cleanEventStream(now)
  64. EVENT_LAST_CLEAN_TIMESTAMP = now
  65. }
  66. return previous
  67. },
  68. _cleanEventStream(now) {
  69. Object.entries(EVENT_LOG_TIMESTAMP).forEach(([key, timestamp]) => {
  70. if (now - timestamp > EventLogger.MAX_STALE_TIME_IN_MS) {
  71. delete EVENT_LOG_COUNTER[key]
  72. delete EVENT_LOG_TIMESTAMP[key]
  73. }
  74. })
  75. }
  76. }