EventLogger.js 2.2 KB

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