+ liblatency: Added utils_latency_config code
authorPavel Rochnyack <pavel2000@ngs.ru>
Sat, 7 May 2016 17:26:15 +0000 (23:26 +0600)
committerFlorian Forster <octo@collectd.org>
Sun, 27 Nov 2016 06:55:28 +0000 (07:55 +0100)
+ tail plugin: DSType Latency added

src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/daemon/Makefile.am
src/daemon/utils_match.c
src/daemon/utils_match.h
src/daemon/utils_tail_match.c
src/daemon/utils_tail_match.h
src/tail.c
src/utils_latency_config.c [new file with mode: 0644]
src/utils_latency_config.h [new file with mode: 0644]

index fe2d2a8..a01176d 100644 (file)
@@ -52,7 +52,8 @@ test_format_json_LDADD = libformat_json.la daemon/libmetadata.la daemon/libplugi
 endif
 
 noinst_LTLIBRARIES += liblatency.la
-liblatency_la_SOURCES = utils_latency.c utils_latency.h
+liblatency_la_SOURCES = utils_latency.c utils_latency.h utils_latency_config.c utils_latency_config.h
+liblatency_la_LIBADD = daemon/libcommon.la
 check_PROGRAMS += test_utils_latency
 TESTS += test_utils_latency
 test_utils_latency_SOURCES = utils_latency_test.c testing.h
@@ -1115,6 +1116,7 @@ if BUILD_PLUGIN_TAIL
 pkglib_LTLIBRARIES += tail.la
 tail_la_SOURCES = tail.c
 tail_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+tail_la_LIBADD = liblatency.la
 endif
 
 if BUILD_PLUGIN_TAIL_CSV
index 719010f..037371b 100644 (file)
 #      Instance "local_user"
 #    </Match>
 #  </File>
+#  <File "/var/log/nginx/apache-time.log">
+#    #Use the following log format in nginx:
+#    #log_format response_time '[$host] "$upstream_response_time" ...'
+#    Instance "apache"
+#    <Match>
+#      Regex "^\\S+ \"([0-9.]+)\""
+#      DSType "Latency"
+#      Type "response_time"
+#      #LatencyPercentileType "percentile"
+#      LatencyPercentile 5
+#      LatencyPercentile 30
+#      LatencyPercentile 50
+#      LatencyPercentile 70
+#      LatencyPercentile 95
+#      LatencyRateType "requests"
+#      LatencyRate 0.001 0.100
+#      LatencyRate 0.101 0.200
+#      LatencyRate 0.201 0
+#    </Match>
+#  </File>
 #</Plugin>
 
 #<Plugin tail_csv>
index 4293abf..0f99169 100644 (file)
@@ -7187,6 +7187,14 @@ Increase the internal counter by one. These B<DSType> are the only ones that do
 not use the matched subexpression, but simply count the number of matched
 lines. Thus, you may use a regular expression without submatch in this case.
 
+=item B<Latency>
+
+Special type to handle latency values from logfiles. The matched value must be
+latency in seconds, floating point numbers are supported.
+Should be used with B<LatencyPercentile> or B<LatencyRate> options.
+
+The B<Instance> option cannot be used together with B<DSType> B<Latency>.
+
 =back
 
 As you'd expect the B<Gauge*> types interpret the submatch as a floating point
@@ -7205,6 +7213,29 @@ their configuration can be found in L<types.db(5)>.
 
 This optional setting sets the type instance to use.
 
+=item B<LatencyPercentile> I<Percent>
+
+Calculate and dispatch the configured percentile, i.e. compute the latency, so
+that I<Percent> of all matched latency values are smaller than or equal to the
+computed latency.
+
+Different percentiles can be calculated by setting this option several times.
+
+=item B<LatencyPercentileType> I<Type>
+Sets the type used to dispatch B<LatencyPercentile> values.
+
+=item B<LatencyRate> I<lower_latency> I<upper_latency>
+
+Calculate and dispatch rate of latency values fall within requested interval.
+Interval specified as [I<lower_latency>,I<upper_latency>] (including boundaries).
+When I<upper_latency> value is equal to 0 then interval is [lower, infinity).
+
+Rates for different intervals can be calculated by setting this option several
+times.
+
+=item B<LatencyRateType> I<Type>
+Sets the type used to dispatch B<LatencyRate> values.
+
 =back
 
 =head2 Plugin C<tail_csv>
index cb62c64..ac8c764 100644 (file)
@@ -67,7 +67,9 @@ collectd_SOURCES = collectd.c collectd.h \
                   utils_tail.c utils_tail.h \
                   utils_time.c utils_time.h \
                   types_list.c types_list.h \
-                  utils_threshold.c utils_threshold.h
+                  utils_threshold.c utils_threshold.h \
+                  ../utils_latency_config.h ../utils_latency_config.c \
+                  ../utils_latency.h ../utils_latency.c
 
 
 collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
index 2a39e4d..98d9311 100644 (file)
@@ -99,6 +99,13 @@ static int default_callback (const char __attribute__((unused)) *str,
     if (matches[1] == endptr)
       return (-1);
 
+    if (data->ds_type & UTILS_MATCH_CF_GAUGE_LATENCY)
+    {
+      latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value));
+      data->values_num++;
+      return (0);
+    }
+
     if ((data->values_num == 0)
        || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST)
        || (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST))
@@ -228,6 +235,10 @@ static int default_callback (const char __attribute__((unused)) *str,
 
 static void match_simple_free (void *data)
 {
+  cu_match_value_t *user_data = (cu_match_value_t *) data;
+  if (user_data->latency)
+    latency_counter_destroy(user_data->latency);
+
   free (data);
 } /* void match_simple_free */
 
@@ -287,10 +298,25 @@ cu_match_t *match_create_simple (const char *regex,
     return (NULL);
   user_data->ds_type = match_ds_type;
 
+  if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+      && (match_ds_type & UTILS_MATCH_CF_GAUGE_LATENCY))
+  {
+    user_data->latency = latency_counter_create();
+    if (user_data->latency == NULL)
+    {
+      ERROR ("match_create_simple(): latency_counter_create() failed.");
+      free (user_data);
+      return (NULL);
+    }
+  }
+
   obj = match_create_callback (regex, excluderegex,
                               default_callback, user_data, match_simple_free);
   if (obj == NULL)
   {
+    if (user_data->latency)
+      latency_counter_destroy(user_data->latency);
+
     sfree (user_data);
     return (NULL);
   }
index 31dc66d..f8aba2c 100644 (file)
@@ -28,6 +28,8 @@
 #define UTILS_MATCH_H 1
 
 #include "plugin.h"
+#include "utils_latency.h"
+#include "utils_latency_config.h"
 
 /*
  * Each type may have 12 sub-types
@@ -47,6 +49,7 @@
 #define UTILS_MATCH_CF_GAUGE_INC     0x10
 #define UTILS_MATCH_CF_GAUGE_ADD     0x20
 #define UTILS_MATCH_CF_GAUGE_PERSIST 0x40
+#define UTILS_MATCH_CF_GAUGE_LATENCY 0x80
 
 #define UTILS_MATCH_CF_COUNTER_SET   0x01
 #define UTILS_MATCH_CF_COUNTER_ADD   0x02
@@ -71,6 +74,7 @@ struct cu_match_value_s
   int ds_type;
   value_t value;
   unsigned int values_num;
+  latency_counter_t *latency;
 };
 typedef struct cu_match_value_s cu_match_value_t;
 
index 99d5dec..0be8cfe 100644 (file)
@@ -36,6 +36,7 @@
 #include "utils_match.h"
 #include "utils_tail.h"
 #include "utils_tail_match.h"
+#include "utils_latency_config.h"
 
 struct cu_tail_match_simple_s
 {
@@ -44,6 +45,7 @@ struct cu_tail_match_simple_s
   char type[DATA_MAX_NAME_LEN];
   char type_instance[DATA_MAX_NAME_LEN];
   cdtime_t interval;
+  latency_config_t latency_config;
 };
 typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
 
@@ -102,6 +104,95 @@ static int simple_submit_match (cu_match_t *match, void *user_data)
   return (0);
 } /* int simple_submit_match */
 
+static int simple_submit_latency (cu_match_t *match, void *user_data)
+{
+  cu_tail_match_simple_t *data = (cu_tail_match_simple_t *) user_data;
+  cu_match_value_t *match_value;
+  value_list_t vl = VALUE_LIST_INIT;
+  value_t values[1];
+
+  match_value = (cu_match_value_t *) match_get_user_data (match);
+  if (match_value == NULL)
+    return (-1);
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, data->plugin, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, data->plugin_instance,
+      sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, data->type, sizeof (vl.type));
+  vl.interval = data->interval;
+  vl.time = cdtime ();
+
+  if (data->latency_config.lower) {
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "lower");
+    values[0].gauge = (match_value->values_num != 0)
+      ? CDTIME_T_TO_DOUBLE (latency_counter_get_min (match_value->latency))
+      : NAN;
+    plugin_dispatch_values (&vl);
+  }
+
+  if (data->latency_config.avg) {
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "average");
+    values[0].gauge = (match_value->values_num != 0)
+      ? CDTIME_T_TO_DOUBLE (latency_counter_get_average (match_value->latency))
+      : NAN;
+    plugin_dispatch_values (&vl);
+  }
+
+  if (data->latency_config.upper) {
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "upper");
+    values[0].gauge = (match_value->values_num != 0)
+      ? CDTIME_T_TO_DOUBLE (latency_counter_get_max (match_value->latency))
+      : NAN;
+    plugin_dispatch_values (&vl);
+  }
+
+  size_t i;
+  /* Submit percentiles */
+  if (data->latency_config.percentile_type != NULL)
+    sstrncpy (vl.type, data->latency_config.percentile_type, sizeof (vl.type));
+  for (i = 0; i < data->latency_config.percentile_num; i++)
+  {
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "percentile-%.0f",  data->latency_config.percentile[i]);
+    values[0].gauge = (match_value->values_num != 0)
+      ? CDTIME_T_TO_DOUBLE (latency_counter_get_percentile (match_value->latency,
+                                            data->latency_config.percentile[i]))
+      : NAN;
+    plugin_dispatch_values (&vl);
+  }
+
+  /* Submit rates */
+  sstrncpy (vl.type, data->type, sizeof (vl.type));
+  if (data->latency_config.rates_type != NULL)
+    sstrncpy (vl.type, data->latency_config.rates_type, sizeof (vl.type));
+  for (i = 0; i < data->latency_config.rates_num; i++)
+  {
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "rate-%.3f-%.3f",
+        CDTIME_T_TO_DOUBLE(data->latency_config.rates[i * 2]),
+        CDTIME_T_TO_DOUBLE(data->latency_config.rates[i * 2 + 1]));
+    values[0].gauge = (match_value->values_num != 0) 
+      ? latency_counter_get_rate (match_value->latency,
+                                  data->latency_config.rates[i * 2],
+                                  data->latency_config.rates[i * 2 + 1],
+                                  vl.time)
+      : NAN;
+    plugin_dispatch_values (&vl);
+  }
+  latency_counter_reset (match_value->latency);
+
+  match_value->value.gauge = NAN;
+  match_value->values_num = 0;
+
+  return (0);
+} /* int simple_submit_latency */
+
 static int tail_callback (void *data, char *buf,
     int __attribute__((unused)) buflen)
 {
@@ -113,6 +204,13 @@ static int tail_callback (void *data, char *buf,
   return (0);
 } /* int tail_callback */
 
+static void tail_match_simple_free (void *data)
+{
+  cu_tail_match_simple_t *user_data = (cu_tail_match_simple_t *) data;
+  latency_config_free(user_data->latency_config);
+  sfree (user_data);
+} /* void tail_match_simple_free */
+
 /*
  * Public functions
  */
@@ -193,7 +291,9 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
 int tail_match_add_match_simple (cu_tail_match_t *obj,
     const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance, const cdtime_t interval)
+    const char *type, const char *type_instance,
+    const latency_config_t latency_cfg,
+    const cdtime_t interval)
 {
   cu_match_t *match;
   cu_tail_match_simple_t *user_data;
@@ -222,13 +322,29 @@ int tail_match_add_match_simple (cu_tail_match_t *obj,
 
   user_data->interval = interval;
 
-  status = tail_match_add_match (obj, match, simple_submit_match,
+  if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+      && (ds_type & UTILS_MATCH_CF_GAUGE_LATENCY))
+  {
+    status = latency_config_copy(&user_data->latency_config, latency_cfg);
+    if (status != 0)
+    {
+      ERROR ("tail_match_add_match_simple: latency_config_copy() failed.");
+      status = -1;
+      goto out;
+    }
+
+    status = tail_match_add_match (obj, match, simple_submit_latency,
+      user_data, tail_match_simple_free);
+  } else {
+    status = tail_match_add_match (obj, match, simple_submit_match,
       user_data, free);
+  }
 
+out:
   if (status != 0)
   {
+    tail_match_simple_free(user_data);
     match_destroy (match);
-    sfree (user_data);
   }
 
   return (status);
index 0404de2..5601fcb 100644 (file)
@@ -103,6 +103,7 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
  *  passed `plugin', `plugin_instance', `type', and `type_instance' are
  *  directly used when submitting these values.
  *  With excluderegex it is possible to exlude lines from the match.
+ *  The `latency_cfg' specifies configuration for submitting latency.
  *
  * RETURN VALUE
  *   Zero upon success, non-zero otherwise.
@@ -110,7 +111,8 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
 int tail_match_add_match_simple (cu_tail_match_t *obj,
     const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance, const cdtime_t interval);
+    const char *type, const char *type_instance, const latency_config_t latency_cfg,
+    const cdtime_t interval);
 
 /*
  * NAME
index 0ac8be7..5326e7c 100644 (file)
@@ -29,6 +29,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "utils_tail_match.h"
+#include "utils_latency_config.h"
 
 /*
  *  <Plugin tail>
@@ -54,6 +55,7 @@ struct ctail_config_match_s
   char *type;
   char *type_instance;
   cdtime_t interval;
+  latency_config_t latency;
 };
 typedef struct ctail_config_match_s ctail_config_match_t;
 
@@ -90,6 +92,11 @@ static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     else
       cm->flags = 0;
   }
+  else if (strcasecmp ("Latency", ci->values[0].value.string) == 0)
+  {
+    cm->flags = UTILS_MATCH_DS_TYPE_GAUGE;
+    cm->flags |= UTILS_MATCH_CF_GAUGE_LATENCY;
+  }
   else if (strncasecmp ("Counter", ci->values[0].value.string, strlen ("Counter")) == 0)
   {
     cm->flags = UTILS_MATCH_DS_TYPE_COUNTER;
@@ -163,6 +170,28 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
       status = cf_util_get_string (option, &cm.type);
     else if (strcasecmp ("Instance", option->key) == 0)
       status = cf_util_get_string (option, &cm.type_instance);
+    else if (strncasecmp ("Latency", option->key, strlen ("Latency")) == 0)
+    {
+      if (strcasecmp ("LatencyPercentile", option->key) == 0)
+        status = latency_config_add_percentile ("tail", &cm.latency, option);
+      else if (strcasecmp ("LatencyPercentileType", option->key) == 0)
+        status = cf_util_get_string (option, &cm.latency.percentile_type);
+      else if (strcasecmp ("LatencyRate", option->key) == 0)
+        status = latency_config_add_rate ("tail", &cm.latency, option);
+      else if (strcasecmp ("LatencyRateType", option->key) == 0)
+        status = cf_util_get_string (option, &cm.latency.rates_type);
+      else if (strcasecmp ("LatencyLower", option->key) == 0)
+        status = cf_util_get_boolean (option, &cm.latency.lower);
+      else if (strcasecmp ("LatencyUpper", option->key) == 0)
+        status = cf_util_get_boolean (option, &cm.latency.upper);
+      else if (strcasecmp ("LatencyAvg", option->key) == 0)
+        status = cf_util_get_boolean (option, &cm.latency.avg);
+      else 
+      {
+        WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
+        status = -1;
+      }
+    }
     else
     {
       WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
@@ -196,13 +225,35 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
       break;
     }
 
+    if ((cm.flags & UTILS_MATCH_DS_TYPE_GAUGE)
+        && (cm.flags & UTILS_MATCH_CF_GAUGE_LATENCY))
+    {
+
+      if (cm.type_instance != NULL)
+      {
+        WARNING ("tail plugin: `DSType Latency' and `Instance %s' in `Match' "
+                 "block could not be used together.", cm.type_instance);
+        status = -1;
+        break;
+      }
+
+      if (cm.latency.percentile_num == 0 && cm.latency.rates_num == 0)
+      {
+        WARNING ("tail plugin: `Match' with `DSType Latency' has no "
+                 "`LatencyPercentile' or `LatencyRate' options.");
+        status = -1;
+        break;
+      }
+    }
+
     break;
   } /* while (status == 0) */
 
   if (status == 0)
   {
     status = tail_match_add_match_simple (tm, cm.regex, cm.excluderegex,
-       cm.flags, "tail", plugin_instance, cm.type, cm.type_instance, interval);
+      cm.flags, "tail", plugin_instance, cm.type, cm.type_instance,
+      cm.latency, interval);
 
     if (status != 0)
     {
@@ -214,6 +265,7 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
   sfree (cm.excluderegex);
   sfree (cm.type);
   sfree (cm.type_instance);
+  latency_config_free(cm.latency);
 
   return (status);
 } /* int ctail_config_add_match */
diff --git a/src/utils_latency_config.c b/src/utils_latency_config.c
new file mode 100644 (file)
index 0000000..7504aaf
--- /dev/null
@@ -0,0 +1,163 @@
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016   Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ *   Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_latency_config.h"
+
+int latency_config_add_percentile (const char *plugin, latency_config_t *cl,
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    ERROR ("%s plugin: \"%s\" requires exactly one numeric argument.",
+             plugin, ci->key);
+    return (-1);
+  }
+
+  double percent = ci->values[0].value.number;
+  double *tmp;
+
+  if ((percent <= 0.0) || (percent >= 100))
+  {
+    ERROR ("%s plugin: The value for \"%s\" must be between 0 and 100, "
+        "exclusively.", plugin, ci->key);
+    return (ERANGE);
+  }
+
+  tmp = realloc (cl->percentile,
+      sizeof (*cl->percentile) * (cl->percentile_num + 1));
+  if (tmp == NULL)
+  {
+    ERROR ("%s plugin: realloc failed.", plugin);
+    return (ENOMEM);
+  }
+  cl->percentile = tmp;
+  cl->percentile[cl->percentile_num] = percent;
+  cl->percentile_num++;
+
+  return (0);
+} /* int latency_config_add_percentile */
+
+int latency_config_add_rate (const char *plugin, latency_config_t *cl,
+    oconfig_item_t *ci)
+{
+
+  if ((ci->values_num != 2)
+    ||(ci->values[0].type != OCONFIG_TYPE_NUMBER)
+    ||(ci->values[1].type != OCONFIG_TYPE_NUMBER))
+  {
+    ERROR ("%s plugin: \"%s\" requires exactly two numeric arguments.",
+             plugin, ci->key);
+    return (-1);
+  }
+
+  if (ci->values[1].value.number &&
+      ci->values[1].value.number <= ci->values[0].value.number)
+  {
+    ERROR ("%s plugin: MIN must be less than MAX in \"%s\".",
+             plugin, ci->key);
+    return (-1);
+  }
+
+  if (ci->values[0].value.number < 0.001)
+  {
+    ERROR ("%s plugin: MIN must be greater or equal to 0.001 in \"%s\".",
+             plugin, ci->key);
+    return (-1);
+  }
+
+  cdtime_t lower = DOUBLE_TO_CDTIME_T(ci->values[0].value.number);
+  cdtime_t upper = DOUBLE_TO_CDTIME_T(ci->values[1].value.number);
+  cdtime_t *tmp;
+
+  tmp = realloc (cl->rates,
+      sizeof (*cl->rates) * (cl->rates_num + 1) * 2);
+  if (tmp == NULL)
+  {
+    ERROR ("%s plugin: realloc failed.", plugin);
+    return (ENOMEM);
+  }
+  cl->rates = tmp;
+  cl->rates[cl->rates_num * 2] = lower;
+  cl->rates[cl->rates_num * 2 + 1] = upper;
+  cl->rates_num++;
+
+  return (0);
+} /* int latency_config_add_rate */
+
+
+int latency_config_copy (latency_config_t *dst, const latency_config_t src)
+{
+  /* Copy percentiles configuration */
+  dst->percentile_num = src.percentile_num;
+  dst->percentile = malloc(sizeof (*dst->percentile) * (src.percentile_num));
+  if (dst->percentile == NULL)
+    goto nomem;
+
+  memcpy (dst->percentile, src.percentile,
+          (sizeof (*dst->percentile) * (src.percentile_num)));
+
+  if (src.percentile_type != NULL)
+  {
+    dst->percentile_type = strdup(src.percentile_type);
+    if (dst->percentile_type == NULL)
+      goto nomem;
+  }
+
+  /* Copy rates configuration */
+  dst->rates_num = src.rates_num;
+  dst->rates = malloc(sizeof (*dst->rates) * (src.rates_num) * 2);
+  if (dst->rates == NULL)
+    goto nomem;
+
+  memcpy (dst->rates, src.rates,
+          (sizeof (*dst->rates) * (src.rates_num) * 2));
+
+  if (src.rates_type != NULL)
+  {
+    dst->rates_type = strdup(src.rates_type);
+    if (dst->rates_type == NULL)
+      goto nomem;
+  }
+
+  return (0);
+nomem:
+  free (dst->rates);
+  free (dst->rates_type);
+  free (dst->percentile);
+  free (dst->percentile_type);
+  return (-1);
+} /* int latency_config_copy */
+
+void latency_config_free (latency_config_t lc)
+{
+  sfree (lc.rates);
+  sfree (lc.rates_type);
+  sfree (lc.percentile);
+  sfree (lc.percentile_type);
+} /* void latency_config_free */
diff --git a/src/utils_latency_config.h b/src/utils_latency_config.h
new file mode 100644 (file)
index 0000000..283ce1c
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016   Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ *   Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#ifndef UTILS_LATENCY_CONFIG_H
+#define UTILS_LATENCY_CONFIG_H 1
+
+#include "collectd.h"
+#include "utils_time.h"
+
+struct latency_config_s
+{
+  double *percentile;
+  size_t percentile_num;
+  char   *percentile_type;
+  cdtime_t *rates;
+  size_t   rates_num;
+  char     *rates_type;
+  _Bool lower;
+  _Bool upper;
+  //_Bool sum;
+  _Bool avg;
+  //_Bool count;
+};
+typedef struct latency_config_s latency_config_t;
+
+
+int latency_config_add_percentile (const char *plugin, latency_config_t *cl,
+    oconfig_item_t *ci);
+
+int latency_config_add_rate (const char *plugin, latency_config_t *cl,
+    oconfig_item_t *ci);
+
+int latency_config_copy (latency_config_t *dst, const latency_config_t src);
+
+void latency_config_free (latency_config_t lc);
+
+#endif /* UTILS_LATENCY_CONFIG_H */