[collectd] [RFC 2/2] Add DCOUNTER type

David Gibson david at gibson.dropbear.id.au
Sat Aug 13 13:10:24 CEST 2016


This adds a new DCOUNTER ds-type to collectd.  This is similar to the
DCOUNTER type in recent rrdtool versions.  The semantics are similar to
COUNTER, but it has floating point (double) values, instead of integer.

NOTE: This implementation isn't quite like the rrdtool version.  rrdtool
allows DCOUNTER values to count either up or down, but not both.  The
direction is autodetected from the first two valid values, then any change
in the wrong direction is considered a reset: rates won't be calculated
until there are at least two valid values after the reset.

This implementation, however, assumes the DCOUNTER only counts up, and
treats any value less than the previous as a reset.  I don't like this
difference, but so far I haven't been able to figure out how to detect
and store the expected direction.

Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
 contrib/collectd_network.py |  4 ++++
 src/collectd-perl.pod       |  4 +++-
 src/csv.c                   |  9 ++++++++-
 src/curl.c                  | 11 +++++++++++
 src/curl_xml.c              |  4 ++++
 src/daemon/collectd.h       |  4 ++++
 src/daemon/common.c         | 31 ++++++++++++++++++++++++++++++-
 src/daemon/plugin.c         |  3 +++
 src/daemon/plugin.h         |  4 ++++
 src/daemon/types_list.c     |  2 ++
 src/daemon/utils_cache.c    | 17 +++++++++++++++++
 src/daemon/utils_match.c    | 24 ++++++++++++++++++++++++
 src/daemon/utils_match.h    |  4 ++++
 src/java.c                  |  7 ++++++-
 src/perl.c                  |  8 +++++++-
 src/python.c                |  5 ++++-
 src/pyvalues.c              | 14 ++++++++++++++
 src/redis.c                 |  3 +++
 src/rrdcached.c             | 10 ++++++++--
 src/tail.c                  | 10 ++++++++++
 src/target_scale.c          | 13 +++++++++++++
 src/utils_format_graphite.c |  2 ++
 src/utils_format_json.c     |  7 +++++++
 src/utils_format_json.h     |  4 ++++
 src/utils_format_kairosdb.c | 20 ++++++++++++++++++++
 src/utils_format_kairosdb.h |  4 ++++
 src/utils_rrdcreate.c       |  2 ++
 src/write_mongodb.c         |  2 ++
 src/write_riemann.c         |  6 +++++-
 src/write_sensu.c           | 10 +++++++++-
 src/write_tsdb.c            |  2 ++
 31 files changed, 240 insertions(+), 10 deletions(-)

diff --git a/contrib/collectd_network.py b/contrib/collectd_network.py
index fa7f5e9..d14f101 100644
--- a/contrib/collectd_network.py
+++ b/contrib/collectd_network.py
@@ -64,6 +64,7 @@ DS_TYPE_GAUGE        = 1
 DS_TYPE_DERIVE       = 2
 DS_TYPE_ABSOLUTE     = 3
 DS_TYPE_DDERIVE      = 4
+DS_TYPE_DCOUNTER     = 5
 
 header = struct.Struct("!2H")
 number = struct.Struct("!Q")
@@ -98,6 +99,9 @@ def decode_network_values(ptype, plen, buf):
         elif dstype == DS_TYPE_DDERIVE:
             result.append((dstype, number.unpack_from(buf, off)[0]))
             off += valskip
+        elif dstype == DS_TYPE_DCOUNTER:
+            result.append((dstype, number.unpack_from(buf, off)[0]))
+            off += valskip
         else:
             raise ValueError("DS type %i unsupported" % dstype)
 
diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod
index 491f28b..f8d7827 100644
--- a/src/collectd-perl.pod
+++ b/src/collectd-perl.pod
@@ -186,7 +186,7 @@ structure. The general layout looks like this:
 
   [{
     name => 'data_source_name',
-    type => DS_TYPE_COUNTER || DS_TYPE_GAUGE || DS_TYPE_DERIVE || DS_TYPE_ABSOLUTE || DS_TYPE_DDERIVE,
+    type => DS_TYPE_COUNTER || DS_TYPE_GAUGE || DS_TYPE_DERIVE || DS_TYPE_ABSOLUTE || DS_TYPE_DDERIVE || DS_TYPE_DCOUNTER,
     min  => value || undef,
     max  => value || undef
   }, ...]
@@ -569,6 +569,8 @@ available (B<:all> will export all of them):
 
 =item B<DS_TYPE_DDERIVE>
 
+=item B<DS_TYPE_DCOUNTER>
+
 =back
 
 =item B<:log>
diff --git a/src/csv.c b/src/csv.c
index 8c26d47..92f67fc 100644
--- a/src/csv.c
+++ b/src/csv.c
@@ -64,7 +64,8 @@ static int value_list_to_string (char *buffer, int buffer_len,
 				&& (ds->ds[i].type != DS_TYPE_GAUGE)
 				&& (ds->ds[i].type != DS_TYPE_DERIVE)
 				&& (ds->ds[i].type != DS_TYPE_ABSOLUTE)
-				&& (ds->ds[i].type != DS_TYPE_DDERIVE))
+				&& (ds->ds[i].type != DS_TYPE_DDERIVE)
+				&& (ds->ds[i].type != DS_TYPE_DCOUNTER))
 		{
 			sfree (rates);
 			return (-1);
@@ -116,6 +117,12 @@ static int value_list_to_string (char *buffer, int buffer_len,
 					buffer_len - offset,
 					",%lf", vl->values[i].dderive);
 		}
+		else if (ds->ds[i].type == DS_TYPE_DCOUNTER)
+		{
+			status = ssnprintf (buffer + offset,
+					buffer_len - offset,
+					",%lf", vl->values[i].dcounter);
+		}
 
 		if ((status < 1) || (status >= (buffer_len - offset)))
 		{
diff --git a/src/curl.c b/src/curl.c
index 70ce32f..c2772b5 100644
--- a/src/curl.c
+++ b/src/curl.c
@@ -259,6 +259,17 @@ else if (strncasecmp ("DDerive", ci->values[0].value.string,
     else
       dstype = 0;
   }
+else if (strncasecmp ("DCounter", ci->values[0].value.string,
+        strlen ("DCounter")) == 0)
+  {
+    dstype = UTILS_MATCH_DS_TYPE_DCOUNTER;
+    if (strcasecmp ("DCounterSet", ci->values[0].value.string) == 0)
+      dstype |= UTILS_MATCH_CF_DCOUNTER_SET;
+    else if (strcasecmp ("CounterAdd", ci->values[0].value.string) == 0)
+      dstype |= UTILS_MATCH_CF_DCOUNTER_ADD;
+    else
+      dstype = 0;
+  }
 
   else
   {
diff --git a/src/curl_xml.c b/src/curl_xml.c
index 1894c93..1faa837 100644
--- a/src/curl_xml.c
+++ b/src/curl_xml.c
@@ -356,6 +356,10 @@ static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
       vl->values[index].dderive = (dderive_t) strtod (node_value,
           /* endptr = */ NULL);
       break;
+    case DS_TYPE_DCOUNTER:
+      vl->values[index].dcounter = (dcounter_t) strtod (node_value,
+          /* endptr = */ NULL);
+      break;
   }
 
   /* free up object */
diff --git a/src/daemon/collectd.h b/src/daemon/collectd.h
index df1a623..2e73985 100644
--- a/src/daemon/collectd.h
+++ b/src/daemon/collectd.h
@@ -309,6 +309,10 @@ typedef int _Bool;
 # define DDERIVE_FORMAT "%.15g"
 #endif
 
+#ifndef DCOUNTER_FORMAT
+# define DCOUNTER_FORMAT "%.15g"
+#endif
+
 /* Type for time as used by "utils_time.h" */
 typedef uint64_t cdtime_t;
 
diff --git a/src/daemon/common.c b/src/daemon/common.c
index 220201b..5610728 100644
--- a/src/daemon/common.c
+++ b/src/daemon/common.c
@@ -986,6 +986,8 @@ int format_values (char *ret, size_t ret_len, /* {{{ */
                         BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
                 else if (ds->ds[i].type == DS_TYPE_DDERIVE)
                         BUFFER_ADD (":"DDERIVE_FORMAT, vl->values[i].dderive);
+                else if (ds->ds[i].type == DS_TYPE_DCOUNTER)
+                        BUFFER_ADD (":"DCOUNTER_FORMAT, vl->values[i].dcounter);
                 else
                 {
                         ERROR ("format_values: Unknown data source type: %i",
@@ -1123,6 +1125,10 @@ int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
       ret_value->dderive = (dderive_t) strtod (value, &endptr);
       break;
 
+    case DS_TYPE_DCOUNTER:
+      ret_value->dcounter = (dcounter_t) strtod (value, &endptr);
+      break;
+
     default:
       sfree (value);
       ERROR ("parse_value: Invalid data source type: %i.", ds_type);
@@ -1196,6 +1202,8 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
 			vl->values[i].gauge = NAN;
 		else if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_DDERIVE))
 			vl->values[i].dderive = NAN;
+		else if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_DCOUNTER))
+			vl->values[i].dcounter = NAN;
 		else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
 			return -1;
 
@@ -1397,7 +1405,8 @@ int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
 	 * structure. */
 	if ((rate < 0.0)
 			&& ((ds_type == DS_TYPE_COUNTER)
-				|| (ds_type == DS_TYPE_ABSOLUTE)))
+				|| (ds_type == DS_TYPE_ABSOLUTE)
+				|| (ds_type == DS_TYPE_DCOUNTER)))
 	{
 		memset (state, 0, sizeof (*state));
 		return (EINVAL);
@@ -1436,6 +1445,11 @@ int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
 			state->last_value.dderive = (dderive_t) rate;
 			state->residual = 0.0;
 		}
+		else if (ds_type == DS_TYPE_DCOUNTER)
+		{
+			state->last_value.dcounter = (dcounter_t) rate;
+			state->residual = 0.0;
+		}
 		else
 		{
 			assert (23 == 42);
@@ -1473,6 +1487,13 @@ int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
 		state->last_value.dderive += delta_dderive;
 		state->residual = 0.0;
 	}
+	else if (ds_type == DS_TYPE_DCOUNTER)
+	{
+		counter_t delta_dcounter = (dcounter_t) delta_gauge;
+
+		state->last_value.dcounter += delta_dcounter;
+		state->residual = 0.0;
+	}
 	else
 	{
 		assert (23 == 42);
@@ -1530,6 +1551,14 @@ int value_to_rate (gauge_t *ret_rate, /* {{{ */
 		*ret_rate = ((gauge_t) diff) / ((gauge_t) interval);
 		break;
 	}
+	case DS_TYPE_DCOUNTER: {
+		dcounter_t diff = value.dcounter - state->last_value.dcounter;
+		if (value.dcounter < state->last_value.dcounter)
+			return EAGAIN;
+		*ret_rate = ((gauge_t) diff) / ((gauge_t) interval);
+		break;
+	}
+
 	default:
 		return EINVAL;
 	}
diff --git a/src/daemon/plugin.c b/src/daemon/plugin.c
index 5b35fe4..7492044 100644
--- a/src/daemon/plugin.c
+++ b/src/daemon/plugin.c
@@ -2415,6 +2415,9 @@ int plugin_dispatch_multivalue (value_list_t const *template, /* {{{ */
 		case DS_TYPE_DDERIVE:
 			vl->values[0].dderive  = va_arg (ap, dderive_t);
 			break;
+		case DS_TYPE_DCOUNTER:
+			vl->values[0].dcounter  = va_arg (ap, dcounter_t);
+			break;
 		default:
 			ERROR ("plugin_dispatch_multivalue: given store_type is incorrect.");
 			failed++;
diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h
index 96bd2f5..e87940e 100644
--- a/src/daemon/plugin.h
+++ b/src/daemon/plugin.h
@@ -47,12 +47,14 @@
 #define DS_TYPE_DERIVE   2
 #define DS_TYPE_ABSOLUTE 3
 #define DS_TYPE_DDERIVE  4
+#define DS_TYPE_DCOUNTER 5
 
 #define DS_TYPE_TO_STRING(t) (t == DS_TYPE_COUNTER)     ? "counter"  : \
 				(t == DS_TYPE_GAUGE)    ? "gauge"    : \
 				(t == DS_TYPE_DERIVE)   ? "derive"   : \
 				(t == DS_TYPE_ABSOLUTE) ? "absolute" : \
 				(t == DS_TYPE_DDERIVE)  ? "dderive"  : \
+				(t == DS_TYPE_DCOUNTER) ? "dcounter" : \
 				"unknown"
 
 
@@ -88,6 +90,7 @@ typedef double gauge_t;
 typedef int64_t derive_t;
 typedef uint64_t absolute_t;
 typedef double dderive_t;
+typedef double dcounter_t;
 
 union value_u
 {
@@ -96,6 +99,7 @@ union value_u
 	derive_t   derive;
 	absolute_t absolute;
 	dderive_t  dderive;
+	dcounter_t dcounter;
 };
 typedef union value_u value_t;
 
diff --git a/src/daemon/types_list.c b/src/daemon/types_list.c
index ed0726a..43931be 100644
--- a/src/daemon/types_list.c
+++ b/src/daemon/types_list.c
@@ -81,6 +81,8 @@ static int parse_ds (data_source_t *dsrc, char *buf, size_t buf_len)
     dsrc->type = DS_TYPE_ABSOLUTE;
   else if (strcasecmp (fields[1], "DDERIVE") == 0)
     dsrc->type = DS_TYPE_DDERIVE;
+  else if (strcasecmp (fields[1], "DCOUNTER") == 0)
+    dsrc->type = DS_TYPE_DCOUNTER;
   else
   {
     ERROR ("(fields[1] = %s) != (GAUGE || COUNTER || DERIVE || ABSOLUTE)", fields[1]);
diff --git a/src/daemon/utils_cache.c b/src/daemon/utils_cache.c
index 3ab1b23..d7534be 100644
--- a/src/daemon/utils_cache.c
+++ b/src/daemon/utils_cache.c
@@ -204,6 +204,11 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl,
 	ce->values_raw[i].dderive = vl->values[i].dderive;
 	break;
 
+      case DS_TYPE_DCOUNTER:
+	ce->values_gauge[i] = NAN;
+	ce->values_raw[i].dcounter = vl->values[i].dcounter;
+	break;
+
       default:
 	/* This shouldn't happen. */
 	ERROR ("uc_insert: Don't know how to handle data source type %i.",
@@ -459,6 +464,18 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
 	}
 	break;
 
+      case DS_TYPE_DCOUNTER:
+	{
+	  dcounter_t diff = vl->values[i].dcounter - ce->values_raw[i].dcounter;
+
+	  if (vl->values[i].dcounter < ce->values_raw[i].dcounter)
+	    return (-1);
+	  ce->values_gauge[i] = ((double) diff)
+	    / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
+	  ce->values_raw[i].dcounter = vl->values[i].dcounter;
+	}
+	break;
+
       default:
 	/* This shouldn't happen. */
 	pthread_mutex_unlock (&cache_lock);
diff --git a/src/daemon/utils_match.c b/src/daemon/utils_match.c
index 1dc5f79..84f12b9 100644
--- a/src/daemon/utils_match.c
+++ b/src/daemon/utils_match.c
@@ -240,6 +240,30 @@ static int default_callback (const char __attribute__((unused)) *str,
 
     data->values_num++;
   }
+  else if (data->ds_type & UTILS_MATCH_DS_TYPE_DCOUNTER)
+  {
+    dcounter_t value;
+    char *endptr = NULL;
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = (dcounter_t) strtod (matches[1], &endptr);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if (data->ds_type & UTILS_MATCH_CF_DCOUNTER_SET)
+      data->value.dcounter = value;
+    else if (data->ds_type & UTILS_MATCH_CF_DCOUNTER_ADD)
+      data->value.dcounter += value;
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
   else
   {
     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
diff --git a/src/daemon/utils_match.h b/src/daemon/utils_match.h
index 68dbe41..ced7a8f 100644
--- a/src/daemon/utils_match.h
+++ b/src/daemon/utils_match.h
@@ -40,6 +40,7 @@
 #define UTILS_MATCH_DS_TYPE_DERIVE   0x04000
 #define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x08000
 #define UTILS_MATCH_DS_TYPE_DDERIVE  0x10000
+#define UTILS_MATCH_DS_TYPE_DCOUNTER 0x20000
 
 #define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
 #define UTILS_MATCH_CF_GAUGE_MIN     0x02
@@ -63,6 +64,9 @@
 #define UTILS_MATCH_CF_DDERIVE_SET   0x01
 #define UTILS_MATCH_CF_DDERIVE_ADD   0x02
 
+#define UTILS_MATCH_CF_DCOUNTER_SET   0x01
+#define UTILS_MATCH_CF_DCOUNTER_ADD   0x02
+
 /*
  * Data types
  */
diff --git a/src/java.c b/src/java.c
index 04d7dd2..f2c5bb3 100644
--- a/src/java.c
+++ b/src/java.c
@@ -309,6 +309,8 @@ static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
     return (ctoj_jlong_to_number (jvm_env, (jlong) value.absolute));
   else if (ds_type == DS_TYPE_DDERIVE)
     return (ctoj_jlong_to_number (jvm_env, (jdouble) value.dderive));
+  else if (ds_type == DS_TYPE_DCOUNTER)
+    return (ctoj_jlong_to_number (jvm_env, (jdouble) value.dcounter));
   else
     return (NULL);
 } /* }}} jobject ctoj_value_to_number */
@@ -1066,7 +1068,8 @@ static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
 
   if ((ds_type == DS_TYPE_GAUGE)
-      || (ds_type == DS_TYPE_DDERIVE))
+      || (ds_type == DS_TYPE_DDERIVE)
+      || (ds_type == DS_TYPE_DCOUNTER))
   {
     jdouble tmp_double;
 
@@ -1082,6 +1085,8 @@ static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
       (*ret_value).gauge = (gauge_t) tmp_double;
     else if (ds_type == DS_TYPE_DDERIVE)
       (*ret_value).dderive = (gauge_t) tmp_double;
+    else /* if (ds_type == DS_TYPE_DCOUNTER) */
+      (*ret_value).dcounter = (gauge_t) tmp_double;
   }
   else
   {
diff --git a/src/perl.c b/src/perl.c
index 6673e2e..5ea17d8 100644
--- a/src/perl.c
+++ b/src/perl.c
@@ -217,6 +217,7 @@ struct {
 	{ "Collectd::DS_TYPE_DERIVE",     DS_TYPE_DERIVE },
 	{ "Collectd::DS_TYPE_ABSOLUTE",   DS_TYPE_ABSOLUTE },
 	{ "Collectd::DS_TYPE_DDERIVE",    DS_TYPE_DDERIVE },
+	{ "Collectd::DS_TYPE_DCOUNTER",   DS_TYPE_DCOUNTER },
 	{ "Collectd::LOG_ERR",            LOG_ERR },
 	{ "Collectd::LOG_WARNING",        LOG_WARNING },
 	{ "Collectd::LOG_NOTICE",         LOG_NOTICE },
@@ -285,7 +286,8 @@ static int hv2data_source (pTHX_ HV *hash, data_source_t *ds)
 				&& (DS_TYPE_GAUGE != ds->type)
 				&& (DS_TYPE_DERIVE != ds->type)
 				&& (DS_TYPE_ABSOLUTE != ds->type)
-				&& (DS_TYPE_DDERIVE != ds->type)) {
+				&& (DS_TYPE_DDERIVE != ds->type)
+				&& (DS_TYPE_DCOUNTER != ds->type)) {
 			log_err ("hv2data_source: Invalid DS type.");
 			return -1;
 		}
@@ -344,6 +346,8 @@ static size_t av2value (pTHX_ char *name, AV *array, value_t *value, size_t arra
 				value[i].absolute = SvIV (*tmp);
 			else if (DS_TYPE_DDERIVE == ds->ds[i].type)
 				value[i].dderive = SvNV (*tmp);
+			else if (DS_TYPE_DCOUNTER == ds->ds[i].type)
+				value[i].dcounter = SvNV (*tmp);
 		}
 		else {
 			return 0;
@@ -664,6 +668,8 @@ static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
 			val = newSViv (vl->values[i].absolute);
 		else if (DS_TYPE_DDERIVE == ds->ds[i].type)
 			val = newSVnv (vl->values[i].dderive);
+		else if (DS_TYPE_DCOUNTER == ds->ds[i].type)
+			val = newSVnv (vl->values[i].dcounter);
 
 		if (NULL == av_store (values, i, val)) {
 			av_undef (values);
diff --git a/src/python.c b/src/python.c
index fe71162..630b46c 100644
--- a/src/python.c
+++ b/src/python.c
@@ -55,7 +55,7 @@ static char get_ds_doc[] = "get_dataset(name) -> definition\n"
 		"    'name' is a string.\n"
 		"    'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
 		"        DS_TYPE_GAUGE, DS_TYPE_DERIVE, DS_TYPE_ABSOLUTE,\n"
-		"        or DS_TYPE_DDERIVE.\n"
+		"        DS_TYPE_DDERIVE or DS_TYPE_DCOUNTER.\n"
 		"    'min' and 'max' are either a float or None.";
 
 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
@@ -377,6 +377,8 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
 				PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
 			} else if (ds->ds[i].type == DS_TYPE_DDERIVE) {
 				PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].dderive));
+			} else if (ds->ds[i].type == DS_TYPE_DCOUNTER) {
+				PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].dcounter));
 			} else {
 				Py_BEGIN_ALLOW_THREADS
 				ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
@@ -1096,6 +1098,7 @@ static int cpy_init_python(void) {
 	PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
 	PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
 	PyModule_AddStringConstant(module, "DS_TYPE_DDERIVE", DS_TYPE_TO_STRING(DS_TYPE_DDERIVE));
+	PyModule_AddStringConstant(module, "DS_TYPE_DCOUNTER", DS_TYPE_TO_STRING(DS_TYPE_DCOUNTER));
 	return 0;
 }
 
diff --git a/src/pyvalues.c b/src/pyvalues.c
index f634fd9..07c89dc 100644
--- a/src/pyvalues.c
+++ b/src/pyvalues.c
@@ -592,6 +592,13 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
 				Py_XDECREF(num);
 			}
 			break;
+		case DS_TYPE_DCOUNTER:
+			num = PyNumber_Float(item); /* New reference. */
+			if (num != NULL) {
+				value[i].dcounter = PyFloat_AsDouble(num);
+				Py_XDECREF(num);
+			}
+			break;
 		default:
 			free(value);
 			PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds[i].type, value_list.type);
@@ -708,6 +715,13 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
 				Py_XDECREF(num);
 			}
 			break;
+		case DS_TYPE_DCOUNTER:
+			num = PyNumber_Float(item); /* New reference. */
+			if (num != NULL) {
+				value[i].dcounter = PyFloat_AsDouble(num);
+				Py_XDECREF(num);
+			}
+			break;
 		default:
 			free(value);
 			PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds[i].type, value_list.type);
diff --git a/src/redis.c b/src/redis.c
index f1b9c8d..d13c5c1 100644
--- a/src/redis.c
+++ b/src/redis.c
@@ -357,6 +357,9 @@ static int redis_handle_query (redisContext *rh, redis_node_t *rn, redis_query_t
         case DS_TYPE_DDERIVE:
             val.dderive = (dderive_t)rr->integer;
             break;
+        case DS_TYPE_DCOUNTER:
+            val.dcounter = (dcounter_t)rr->integer;
+            break;
         }
         break;
     case REDIS_REPLY_STRING:
diff --git a/src/rrdcached.c b/src/rrdcached.c
index 156f24b..65bca5f 100644
--- a/src/rrdcached.c
+++ b/src/rrdcached.c
@@ -88,7 +88,8 @@ static int value_list_to_string (char *buffer, int buffer_len,
         && (ds->ds[i].type != DS_TYPE_GAUGE)
 	&& (ds->ds[i].type != DS_TYPE_DERIVE)
 	&& (ds->ds[i].type != DS_TYPE_ABSOLUTE)
-	&& (ds->ds[i].type != DS_TYPE_DDERIVE))
+	&& (ds->ds[i].type != DS_TYPE_DDERIVE)
+	&& (ds->ds[i].type != DS_TYPE_DCOUNTER))
       return (-1);
 
     if (ds->ds[i].type == DS_TYPE_COUNTER)
@@ -110,10 +111,15 @@ static int value_list_to_string (char *buffer, int buffer_len,
 	  ":%"PRIu64, vl->values[i].absolute);
 
     }
-    else /* if (ds->ds[i].type == DS_TYPE_DDERIVE) */ {
+    else if (ds->ds[i].type == DS_TYPE_DDERIVE) {
       status = ssnprintf (buffer + offset, buffer_len - offset,
           ":%f", vl->values[i].dderive);
     }
+    else /* if (ds->ds[i].type == DS_TYPE_DCOUNTER) */
+    {
+      status = ssnprintf (buffer + offset, buffer_len - offset,
+          ":%f", vl->values[i].dcounter);
+    }
 
     if ((status < 1) || (status >= (buffer_len - offset)))
       return (-1);
diff --git a/src/tail.c b/src/tail.c
index dbbed8e..ede0307 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -130,6 +130,16 @@ static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     else
       cm->flags = 0;
   }
+  else if (strncasecmp ("DCounter", ci->values[0].value.string, strlen ("DCounter")) == 0)
+  {
+    cm->flags = UTILS_MATCH_DS_TYPE_DCOUNTER;
+    if (strcasecmp ("DCounterSet", ci->values[0].value.string) == 0)
+      cm->flags |= UTILS_MATCH_CF_DCOUNTER_SET;
+    else if (strcasecmp ("DCounterAdd", ci->values[0].value.string) == 0)
+      cm->flags |= UTILS_MATCH_CF_DCOUNTER_ADD;
+    else
+      cm->flags = 0;
+  }
   else
   {
     cm->flags = 0;
diff --git a/src/target_scale.c b/src/target_scale.c
index 818635e..43d1f19 100644
--- a/src/target_scale.c
+++ b/src/target_scale.c
@@ -309,6 +309,17 @@ static int ts_invoke_dderive (const data_set_t *ds, value_list_t *vl, /* {{{ */
 	return (0);
 } /* }}} int ts_invoke_dderive */
 
+static int ts_invoke_dcounter (const data_set_t *ds, value_list_t *vl, /* {{{ */
+		ts_data_t *data, int dsrc_index)
+{
+	if (!isnan (data->factor))
+		vl->values[dsrc_index].dcounter *= data->factor;
+	if (!isnan (data->offset))
+		vl->values[dsrc_index].dcounter += data->offset;
+
+	return (0);
+} /* }}} int ts_invoke_dcounter */
+
 static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
 		oconfig_item_t *ci)
 {
@@ -490,6 +501,8 @@ static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
 			ts_invoke_absolute (ds, vl, data, i);
 		else if (ds->ds[i].type == DS_TYPE_DDERIVE)
 			ts_invoke_dderive (ds, vl, data, i);
+		else if (ds->ds[i].type == DS_TYPE_DCOUNTER)
+			ts_invoke_dcounter (ds, vl, data, i);
 		else
 			ERROR ("Target `scale': Ignoring unknown data source type %i",
 					ds->ds[i].type);
diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c
index a212356..46b8e78 100644
--- a/src/utils_format_graphite.c
+++ b/src/utils_format_graphite.c
@@ -72,6 +72,8 @@ static int gr_format_values (char *ret, size_t ret_len,
         BUFFER_ADD ("%"PRIu64, vl->values[ds_num].absolute);
     else if (ds->ds[ds_num].type == DS_TYPE_DDERIVE)
         BUFFER_ADD (DDERIVE_FORMAT, vl->values[ds_num].dderive);
+    else if (ds->ds[ds_num].type == DS_TYPE_DCOUNTER)
+        BUFFER_ADD (DCOUNTER_FORMAT, vl->values[ds_num].dcounter);
     else
     {
         ERROR ("gr_format_values plugin: Unknown data source type: %i",
diff --git a/src/utils_format_json.c b/src/utils_format_json.c
index e45f6df..a7c2845 100644
--- a/src/utils_format_json.c
+++ b/src/utils_format_json.c
@@ -156,6 +156,13 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
       else
         BUFFER_ADD ("null");
     }
+    else if (ds->ds[i].type == DS_TYPE_DCOUNTER)
+    {
+      if(isfinite (vl->values[i].dcounter))
+        BUFFER_ADD (JSON_DCOUNTER_FORMAT, vl->values[i].dcounter);
+      else
+        BUFFER_ADD ("null");
+    }
     else
     {
       ERROR ("format_json: Unknown data source type: %i",
diff --git a/src/utils_format_json.h b/src/utils_format_json.h
index 2091c7e..a9bad5b 100644
--- a/src/utils_format_json.h
+++ b/src/utils_format_json.h
@@ -39,6 +39,10 @@
 # define JSON_DDERIVE_FORMAT DDERIVE_FORMAT
 #endif
 
+#ifndef JSON_DCOUNTER_FORMAT
+# define JSON_DCOUNTER_FORMAT DCOUNTER_FORMAT
+#endif
+
 int format_json_initialize (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free);
 int format_json_value_list (char *buffer,
diff --git a/src/utils_format_kairosdb.c b/src/utils_format_kairosdb.c
index f073d8c..f8247fa 100644
--- a/src/utils_format_kairosdb.c
+++ b/src/utils_format_kairosdb.c
@@ -220,6 +220,26 @@ static int values_to_kairosdb (char *buffer, size_t buffer_size, /* {{{ */
       return (-1);
     }
   }
+  else if (ds->ds[ds_idx].type == DS_TYPE_DCOUNTER)
+  {
+    if (isfinite (vl->values[ds_idx].dcounter))
+    {
+      BUFFER_ADD ("[[");
+      BUFFER_ADD ("%"PRIu64, CDTIME_T_TO_MS (vl->time));
+      BUFFER_ADD (",");
+      BUFFER_ADD (JSON_DCOUNTER_FORMAT, vl->values[ds_idx].dcounter);
+    }
+    else
+    {
+      DEBUG ("utils_format_kairosdb: invalid vl->values[ds_idx].dcounter for %s|%s|%s|%s|%s",
+                vl->plugin,
+                vl->plugin_instance,
+                vl->type,
+                vl->type_instance,
+                ds->ds[ds_idx].name);
+      return (-1);
+    }
+  }
   else
   {
     ERROR ("format_kairosdb: Unknown data source type: %i",
diff --git a/src/utils_format_kairosdb.h b/src/utils_format_kairosdb.h
index dc8ffab..e52264a 100644
--- a/src/utils_format_kairosdb.h
+++ b/src/utils_format_kairosdb.h
@@ -39,6 +39,10 @@
 # define JSON_DDERIVE_FORMAT DDERIVE_FORMAT
 #endif
 
+#ifndef JSON_DCOUNTER_FORMAT
+# define JSON_DCOUNTER_FORMAT DCOUNTER_FORMAT
+#endif
+
 int format_kairosdb_initialize (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free);
 int format_kairosdb_value_list (char *buffer,
diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c
index 2f23d97..926841c 100644
--- a/src/utils_rrdcreate.c
+++ b/src/utils_rrdcreate.c
@@ -312,6 +312,8 @@ static int ds_get (char ***ret, /* {{{ */
       type = "ABSOLUTE";
     else if (d->type == DS_TYPE_DDERIVE)
       type = "DDERIVE";
+    else if (d->type == DS_TYPE_DCOUNTER)
+      type = "DCOUNTER";
     else
     {
       ERROR ("rrdtool plugin: Unknown DS type: %i",
diff --git a/src/write_mongodb.c b/src/write_mongodb.c
index 7b6e2e9..c8e222b 100644
--- a/src/write_mongodb.c
+++ b/src/write_mongodb.c
@@ -125,6 +125,8 @@ static bson *wm_create_bson (const data_set_t *ds, /* {{{ */
       bson_append_long(ret, key, vl->values[i].absolute);
     else if (ds->ds[i].type == DS_TYPE_DDERIVE)
       bson_append_double(ret, key, vl->values[i].dderive);
+    else if (ds->ds[i].type == DS_TYPE_DCOUNTER)
+      bson_append_double(ret, key, vl->values[i].dcounter);
     else
       assert (23 == 42);
   }
diff --git a/src/write_riemann.c b/src/write_riemann.c
index 4dbb259..c11b8fd 100644
--- a/src/write_riemann.c
+++ b/src/write_riemann.c
@@ -372,9 +372,13 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */
     riemann_event_set(event, RIEMANN_EVENT_FIELD_METRIC_D, (double)rates[index],
                       RIEMANN_EVENT_FIELD_NONE);
   } else if (ds->ds[index].type == DS_TYPE_DDERIVE) {
+	     || (ds->ds[index].type == DS_TYPE_DCOUNTER)) {
     double metric;
 
-    metric = vl->values[index].dderive;
+    if (ds->ds[index].type == DS_TYPE_DDERIVE)
+      metric = vl->values[index].dderive;
+    else
+      metric = vl->values[index].dcounter;
 
     riemann_event_set(event, RIEMANN_EVENT_FIELD_METRIC_D,
 		      metric, RIEMANN_EVENT_FIELD_NONE);
diff --git a/src/write_sensu.c b/src/write_sensu.c
index 0c3a881..4cca52d 100644
--- a/src/write_sensu.c
+++ b/src/write_sensu.c
@@ -507,7 +507,7 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */
 				return NULL;
 			}
 		}
-		else /* if (ds->ds[index].type == DS_TYPE_DDERIVE) */ {
+		else if (ds->ds[index].type == DS_TYPE_DDERIVE) {
 			res = my_asprintf(&value_str, DDERIVE_FORMAT, vl->values[index].dderive);
 			if (res == -1) {
 				free(ret_str);
@@ -515,6 +515,14 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */
 				return NULL;
 			}
 		}
+		else /* if (ds->ds[index].type == DS_TYPE_DCOUNTER) */ {
+			res = my_asprintf(&value_str, DCOUNTER_FORMAT, vl->values[index].dcounter);
+			if (res == -1) {
+				free(ret_str);
+				ERROR("write_sensu plugin: Unable to alloc memory");
+				return NULL;
+			}
+		}
 	}
 
 	// Generate the full service name
diff --git a/src/write_tsdb.c b/src/write_tsdb.c
index 8db0551..ba41b75 100644
--- a/src/write_tsdb.c
+++ b/src/write_tsdb.c
@@ -324,6 +324,8 @@ static int wt_format_values(char *ret, size_t ret_len,
         BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
     else if (ds->ds[ds_num].type == DS_TYPE_DDERIVE)
         BUFFER_ADD(DDERIVE_FORMAT, vl->values[ds_num].dderive);
+    else if (ds->ds[ds_num].type == DS_TYPE_DCOUNTER)
+        BUFFER_ADD(DCOUNTER_FORMAT, vl->values[ds_num].dcounter);
     else
     {
         ERROR("format_values plugin: Unknown data source type: %i",
-- 
2.7.4




More information about the collectd mailing list