prom_wrapper.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /*
  2. * decaffeinate suggestions:
  3. * DS101: Remove unnecessary use of Array.from
  4. * DS102: Remove unnecessary code created because of implicit returns
  5. * DS205: Consider reworking code to avoid use of IIFEs
  6. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  7. */
  8. const prom = require('prom-client');
  9. const registry = require('prom-client').register;
  10. const metrics = new Map();
  11. const optsKey = function(opts) {
  12. let keys = Object.keys(opts);
  13. if (keys.length === 0) { return ''; }
  14. keys = keys.sort();
  15. let hash = '';
  16. for (let key of Array.from(keys)) {
  17. if (hash.length) { hash += ","; }
  18. hash += `${key}:${opts[key]}`;
  19. }
  20. return hash;
  21. };
  22. const extendOpts = function(opts, labelNames) {
  23. for (let label of Array.from(labelNames)) {
  24. if (!opts[label]) { opts[label] = ''; }
  25. }
  26. return opts;
  27. };
  28. const optsAsArgs = function(opts, labelNames) {
  29. const args = [];
  30. for (let label of Array.from(labelNames)) {
  31. args.push(opts[label] || '');
  32. }
  33. return args;
  34. };
  35. const PromWrapper = {
  36. ttlInMinutes: 0,
  37. registry,
  38. metric(type, name) {
  39. return metrics.get(name) || new MetricWrapper(type, name);
  40. },
  41. collectDefaultMetrics: prom.collectDefaultMetrics
  42. };
  43. class MetricWrapper {
  44. constructor(type, name) {
  45. metrics.set(name, this);
  46. this.name = name;
  47. this.instances = new Map();
  48. this.lastAccess = new Date();
  49. this.metric = (() => { switch (type) {
  50. case "counter":
  51. return new prom.Counter({
  52. name,
  53. help: name,
  54. labelNames: ['app','host','status','method', 'path']
  55. });
  56. case "summary":
  57. return new prom.Summary({
  58. name,
  59. help: name,
  60. maxAgeSeconds: 60,
  61. ageBuckets: 10,
  62. labelNames: ['app', 'host', 'path', 'status_code', 'method', 'collection', 'query']
  63. });
  64. case "gauge":
  65. return new prom.Gauge({
  66. name,
  67. help: name,
  68. labelNames: ['app','host', 'status']
  69. });
  70. } })();
  71. }
  72. inc(opts, value) {
  73. return this._execMethod('inc', opts, value);
  74. }
  75. observe(opts, value) {
  76. return this._execMethod('observe', opts, value);
  77. }
  78. set(opts, value) {
  79. return this._execMethod('set', opts, value);
  80. }
  81. sweep() {
  82. const thresh = new Date(Date.now() - (1000 * 60 * PromWrapper.ttlInMinutes));
  83. this.instances.forEach((instance, key) => {
  84. if (thresh > instance.time) {
  85. if (process.env['DEBUG_METRICS']) {
  86. console.log("Sweeping stale metric instance", this.name, {opts: instance.opts}, key);
  87. }
  88. return this.metric.remove(...Array.from(optsAsArgs(instance.opts, this.metric.labelNames) || []));
  89. }
  90. });
  91. if (thresh > this.lastAccess) {
  92. if (process.env['DEBUG_METRICS']) {
  93. console.log("Sweeping stale metric", this.name, thresh, this.lastAccess);
  94. }
  95. metrics.delete(this.name);
  96. return registry.removeSingleMetric(this.name);
  97. }
  98. }
  99. _execMethod(method, opts, value) {
  100. opts = extendOpts(opts, this.metric.labelNames);
  101. const key = optsKey(opts);
  102. if (key !== '') { this.instances.set(key, { time: new Date(), opts }); }
  103. this.lastAccess = new Date();
  104. return this.metric[method](opts, value);
  105. }
  106. }
  107. if (!PromWrapper.sweepRegistered) {
  108. if (process.env['DEBUG_METRICS']) {
  109. console.log("Registering sweep method");
  110. }
  111. PromWrapper.sweepRegistered = true;
  112. setInterval(
  113. function() {
  114. if (PromWrapper.ttlInMinutes) {
  115. if (process.env['DEBUG_METRICS']) {
  116. console.log("Sweeping metrics");
  117. }
  118. return metrics.forEach((metric, key) => {
  119. return metric.sweep();
  120. });
  121. }
  122. },
  123. 60000);
  124. }
  125. module.exports = PromWrapper;