Source: components/agecounters.js

  1. "use strict";
  2. const HOUR = 3600;
  3. const DAY = HOUR * 24;
  4. const WEEK = DAY * 7;
  5. const PERIOD_UNITS = {
  6. "h": HOUR,
  7. "d": DAY,
  8. "w": WEEK,
  9. };
  10. const PERIOD_UNIT_KEYS = Object.keys(PERIOD_UNITS);
  11. /**
  12. * A small helper object that counts into buckets the number of calls to its
  13. * <code>bump</code> that fall into the given age categories.
  14. * Counts are maintained within the object, and can be fetched to set
  15. * into a gauge metric object.
  16. *
  17. * This class is useful when exporting metrics that count the number of
  18. * hourly/daily/weekly active instances of various types of object within the
  19. * bridge.
  20. */
  21. class AgeCounters {
  22. /***
  23. * @param {String[]} counterPeriods A set of strings denoting the bucket periods
  24. * used by the gauge. It is in the format of '#X' where # is the integer period and
  25. * X is the unit of time. A unit can be one of 'h, d, w' for hours, days and weeks.
  26. * 7d would be 7 days. If not given, the periods are 1h, 1d and 7d.
  27. */
  28. constructor(counterPeriods) {
  29. if (counterPeriods) {
  30. counterPeriods = Array.from(counterPeriods);
  31. }
  32. counterPeriods = counterPeriods || ["1h", "1d", "7d"];
  33. this.counters = new Map();
  34. this.counterPeriods = counterPeriods;
  35. counterPeriods.forEach((periodKey) => {
  36. if (periodKey.length < 2) {
  37. throw Error("A period must contain a unit.");
  38. }
  39. const unit = periodKey[periodKey.length-1];
  40. if (!PERIOD_UNIT_KEYS.includes(unit)) {
  41. throw Error(`The unit period must be one of '${PERIOD_UNIT_KEYS.join(",")}'`);
  42. }
  43. const number = parseInt(periodKey.substr(0, periodKey.length-1));
  44. if (isNaN(number) || number <= 0) {
  45. throw Error("The period duration must be a positive integer.");
  46. }
  47. this.counters.set(number * PERIOD_UNITS[unit], 0);
  48. });
  49. this.counterPeriods.push("all");
  50. this.counters.set("all", 0);
  51. }
  52. /**
  53. * Increment the values of the internal counters depending on the given age,
  54. * in seconds.
  55. *
  56. * @param {Number} age The age in seconds.
  57. */
  58. bump(age) {
  59. this.counters.forEach((value, key) => {
  60. if (key === "all") {
  61. this.counters.set("all", value + 1);
  62. }
  63. else if (age < key) {
  64. this.counters.set(key, value + 1);
  65. }
  66. });
  67. }
  68. /**
  69. * Fetch the counts in the age buckets and set them as labeled observations in
  70. * the given gauge metric instance.
  71. *
  72. * @param {Gauge} gauge The gauge metric instance.
  73. * @param {Object} morelabels An object containing more labels to add to the
  74. * gauge when setting values.
  75. */
  76. setGauge(gauge, morelabels) {
  77. const counters = this.counters;
  78. let i = 0;
  79. counters.forEach((value) => {
  80. const labels = Object.assign(
  81. {
  82. age: this.counterPeriods[i]
  83. },
  84. morelabels
  85. );
  86. gauge.set(labels, value);
  87. i++;
  88. });
  89. }
  90. }
  91. module.exports = AgeCounters;