TritonDataCenter / node-artedi

a library for measuring fish
2 stars 4 forks source link

Allow for metrics in a MetricVector to be optionally expired or reset to a default value #14

Closed kellymclaughlin closed 6 years ago

kellymclaughlin commented 6 years ago

Add the ability for metrics contained in a MetricVector object to be expired, meaning they are removed from the metrics objects of the MetricVector, or to be reset to a user-specified default value at the end of the expiry period. This could be exposed as additional options accepted when a new metric collector is registered.

The specific use case I have in mind for this is when a gauge is used to track the progress of a finite event or sequence of steps, but the event has no discrete final state that can be observed to indicate completion. Currently a gauge will continue providing a reading of the last known value indefinitely once the metric data is no longer extant. Providing an expiry option would allow a way to detect the completion of the event or sequence and take the action of either removing the metric entirely or setting the value to a default. For example, in the case of a gauge monitoring a sequence of steps this might be setting the value to 0 to show completion.

kellymclaughlin commented 6 years ago

Here's a first cut at the changes for Metric to make this happen:

diff --git a/lib/metric.js b/lib/metric.js
index bd11950..682dd32 100644
--- a/lib/metric.js
+++ b/lib/metric.js
@@ -23,9 +23,31 @@ var lib_provider = require('./provider');
 function Metric(options) {
     mod_assert.optionalObject(options, 'options');
     this.labels = options ? options.labels : {};
-    this.value = 0;
+
+    this.expires = false;
+    if (options && options.expires) {
+        this.expires = options.expires;
+    }
+    this.default_value = 0;
+    if (options && options.default_value) {
+        this.expires = options.expires;
+    }
+    /*
+     * The expiry_period is time period after which the metric  is reset to
+     * its default value if the metric timestamp is not updated due to a call
+     * to the add or set methods.
+     */
+    this.expiry_period = 300;
+    if (options && options.expiry_period) {
+        this.expiry_period = options.expiry_period;
+    }
+
+    this.value = this.default_value;
+
     // ISO 8601 time when this metric was last updated.
     this.timestamp = null;
+
+    this.expiry_timer = null;
 }

 /*
@@ -37,30 +59,55 @@ Metric.prototype.add = function add(num) {
     mod_assert.number(num, 'num');

     var self = this;
+
+    if (this.expiry_timer) {
+        clearTimeout(this.expiry_timer);
+        this.expiry_timer = null;
+    }
+
     lib_provider['metric-add'].fire(function () {
         return ([num, self.labels]);
     });

     this.value += num;
     this.timestamp = new Date().toISOString();
+
+    if (this.expires) {
+        this.expiry_timer = setTimeout(this.resetValue, this.expiry_period);
+    }
 };

 Metric.prototype.set = function set(num) {
     mod_assert.number(num, 'num');

     var self = this;
+
+    if (this.expiry_timer) {
+        clearTimeout(this.expiry_timer);
+        this.expiry_timer = null;
+    }
+
     lib_provider['metric-set'].fire(function () {
         return ([num, self.labels]);
     });

     this.value = num;
     this.timestamp = new Date().toISOString();
+
+    if (this.expires) {
+        this.expiry_timer = setTimeout(this.resetValue, this.expiry_period);
+    }
 };

 Metric.prototype.getValue = function getValue() {
     return (this.value);
 };

+Metric.prototype.resetValue = function resetValue() {
+    this.value = this.default_value;
+    this.timestamp = new Date().toISOString();
+};
+
 module.exports = {
     Metric: Metric
 };

From here it would just be exposing the API in the collector on down to set these options which should be a pretty straightforward process.

kellymclaughlin commented 6 years ago

CR to address this issue is here.