Merge branch 'pr/846'
authorFlorian Forster <octo@collectd.org>
Fri, 4 Dec 2015 10:57:59 +0000 (11:57 +0100)
committerFlorian Forster <octo@collectd.org>
Fri, 4 Dec 2015 10:57:59 +0000 (11:57 +0100)
19 files changed:
bindings/java/org/collectd/java/GenericJMXConfConnection.java
bindings/java/org/collectd/java/GenericJMXConfValue.java
contrib/redhat/collectd.spec
src/Makefile.am
src/collectd-java.pod
src/collectd.conf.in
src/collectd.conf.pod
src/daemon/Makefile.am
src/daemon/plugin.c
src/daemon/utils_time.c
src/daemon/utils_time.h
src/daemon/utils_time_mock.c [deleted file]
src/openvpn.c
src/postgresql.c
src/snmp.c
src/statsd.c
src/testing.h
src/varnish.c
src/write_kafka.c

index ea0f2fa..aced54b 100644 (file)
@@ -52,7 +52,8 @@ class GenericJMXConfConnection
   private String _host = null;
   private String _instance_prefix = null;
   private String _service_url = null;
-  private MBeanServerConnection _jmx_connection = null;
+  private JMXConnector _jmx_connector = null;
+  private MBeanServerConnection _mbean_connection = null;
   private List<GenericJMXConfMBean> _mbeans = null;
 
   /*
@@ -92,55 +93,72 @@ class GenericJMXConfConnection
     return Collectd.getHostname();
   } /* }}} String getHost */
 
-private void connect () /* {{{ */
-{
-  JMXServiceURL service_url;
-  JMXConnector connector;
-  Map environment;
+  private void connect () /* {{{ */
+  {
+    JMXServiceURL service_url;
+    Map<String,Object> environment;
 
-  if (_jmx_connection != null)
-    return;
+    // already connected
+    if (this._jmx_connector != null) {
+      return;
+    }
 
-  environment = null;
-  if (this._password != null)
-  {
-    String[] credentials;
+    environment = null;
+    if (this._password != null)
+    {
+      String[] credentials;
 
-    if (this._username == null)
-      this._username = new String ("monitorRole");
+      if (this._username == null)
+        this._username = new String ("monitorRole");
 
-    credentials = new String[] { this._username, this._password };
+      credentials = new String[] { this._username, this._password };
 
-    environment = new HashMap ();
-    environment.put (JMXConnector.CREDENTIALS, credentials);
-    environment.put(JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER, this.getClass().getClassLoader());
-  }
+      environment = new HashMap<String,Object> ();
+      environment.put (JMXConnector.CREDENTIALS, credentials);
+      environment.put (JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER, this.getClass().getClassLoader());
+    }
 
-  try
-  {
-    service_url = new JMXServiceURL (this._service_url);
-    connector = JMXConnectorFactory.connect (service_url, environment);
-    _jmx_connection = connector.getMBeanServerConnection ();
-  }
-  catch (Exception e)
+    try
+    {
+      service_url = new JMXServiceURL (this._service_url);
+      this._jmx_connector = JMXConnectorFactory.connect (service_url, environment);
+      this._mbean_connection = _jmx_connector.getMBeanServerConnection ();
+    }
+    catch (Exception e)
+    {
+      Collectd.logError ("GenericJMXConfConnection: "
+          + "Creating MBean server connection failed: " + e);
+      disconnect ();
+      return;
+    }
+  } /* }}} void connect */
+
+  private void disconnect () /* {{{ */
   {
-    Collectd.logError ("GenericJMXConfConnection: "
-        + "Creating MBean server connection failed: " + e);
-    return;
-  }
-} /* }}} void connect */
+    try
+    {
+      this._jmx_connector.close();
+    }
+    catch (Exception e)
+    {
+      // It's fine if close throws an exception
+    }
 
-/*
- * public methods
- *
- * <Connection>
- *   Host "tomcat0.mycompany"
- *   ServiceURL "service:jmx:rmi:///jndi/rmi://localhost:17264/jmxrmi"
- *   Collect "java.lang:type=GarbageCollector,name=Copy"
- *   Collect "java.lang:type=Memory"
- * </Connection>
- *
- */
+    this._jmx_connector = null;
+    this._mbean_connection = null;
+  } /* }}} void disconnect */
+
+  /*
+   * public methods
+   *
+   * <Connection>
+   *   Host "tomcat0.mycompany"
+   *   ServiceURL "service:jmx:rmi:///jndi/rmi://localhost:17264/jmxrmi"
+   *   Collect "java.lang:type=GarbageCollector,name=Copy"
+   *   Collect "java.lang:type=Memory"
+   * </Connection>
+   *
+   */
   public GenericJMXConfConnection (OConfigItem ci) /* {{{ */
     throws IllegalArgumentException
   {
@@ -217,9 +235,10 @@ private void connect () /* {{{ */
   {
     PluginData pd;
 
+    // try to connect
     connect ();
 
-    if (this._jmx_connection == null)
+    if (this._mbean_connection == null)
       return;
 
     Collectd.logDebug ("GenericJMXConfConnection.query: "
@@ -234,11 +253,11 @@ private void connect () /* {{{ */
     {
       int status;
 
-      status = this._mbeans.get (i).query (this._jmx_connection, pd,
+      status = this._mbeans.get (i).query (this._mbean_connection, pd,
           this._instance_prefix);
       if (status != 0)
       {
-        this._jmx_connection = null;
+        disconnect ();
         return;
       }
     } /* for */
index 4b42c91..25b70d4 100644 (file)
@@ -28,6 +28,7 @@ package org.collectd.java;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Collection;
 import java.util.Set;
 import java.util.Iterator;
 import java.util.ArrayList;
@@ -39,6 +40,7 @@ import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
 import javax.management.openmbean.OpenType;
 import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
 import javax.management.openmbean.InvalidKeyException;
 
 import org.collectd.api.Collectd;
@@ -68,6 +70,7 @@ class GenericJMXConfValue
   private List<String> _attributes;
   private String _instance_prefix;
   private List<String> _instance_from;
+  private String _plugin_name;
   private boolean _is_table;
 
   /**
@@ -295,6 +298,46 @@ class GenericJMXConfValue
     {
       if (value instanceof CompositeData)
         return (queryAttributeRecursive ((CompositeData) value, attrName));
+      else if (value instanceof TabularData)
+        return (queryAttributeRecursive ((TabularData) value, attrName));
+      else
+        return (null);
+    }
+  } /* }}} queryAttributeRecursive */
+
+  private Object queryAttributeRecursive (TabularData parent, /* {{{ */
+      List<String> attrName)
+  {
+    String key;
+    Object value = null;
+
+    key = attrName.remove (0);
+
+    TabularData tabularData = (TabularData) parent;
+    Collection<CompositeData> table =
+        (Collection<CompositeData>)tabularData.values();
+    for (CompositeData compositeData : table)
+    {
+      if (key.equals(compositeData.get("key")))
+      {
+        value = compositeData.get("value");
+      }
+    }
+    if (null == value)
+    {
+      return (null);
+    }
+
+    if (attrName.size () == 0)
+    {
+      return (value);
+    }
+    else
+    {
+      if (value instanceof CompositeData)
+        return (queryAttributeRecursive ((CompositeData) value, attrName));
+      else if (value instanceof TabularData)
+        return (queryAttributeRecursive ((TabularData) value, attrName));
       else
         return (null);
     }
@@ -341,6 +384,8 @@ class GenericJMXConfValue
     {
       if (value instanceof CompositeData)
         return (queryAttributeRecursive((CompositeData) value, attrNameList));
+      else if (value instanceof TabularData)
+        return (queryAttributeRecursive((TabularData) value, attrNameList));
       else if (value instanceof OpenType)
       {
         OpenType ot = (OpenType) value;
@@ -351,7 +396,7 @@ class GenericJMXConfValue
       else
       {
         Collectd.logError ("GenericJMXConfValue: Received object of "
-            + "unknown class.");
+            + "unknown class. " + attrName + " " + ((value == null)?"null":value.getClass().getName()));
         return (null);
       }
     }
@@ -436,6 +481,7 @@ class GenericJMXConfValue
     this._attributes = new ArrayList<String> ();
     this._instance_prefix = null;
     this._instance_from = new ArrayList<String> ();
+    this._plugin_name = null;
     this._is_table = false;
 
     /*
@@ -485,6 +531,12 @@ class GenericJMXConfValue
         if (tmp != null)
           this._instance_from.add (tmp);
       }
+      else if (child.getKey ().equalsIgnoreCase ("PluginName"))
+      {
+        String tmp = getConfigString (child);
+        if (tmp != null)
+          this._plugin_name = tmp;
+      }
       else
         throw (new IllegalArgumentException ("Unknown option: "
               + child.getKey ()));
@@ -538,6 +590,10 @@ class GenericJMXConfValue
 
     vl = new ValueList (pd);
     vl.setType (this._ds_name);
+    if (this._plugin_name != null)
+    {
+      vl.setPlugin (this._plugin_name);
+    }
 
     /*
      * Build the instnace prefix from the fixed string prefix and the
index 2252181..4dfcc9a 100644 (file)
 %define with_nginx 0%{!?_without_nginx:1}
 %define with_notify_desktop 0%{!?_without_notify_desktop:1}
 %define with_notify_email 0%{!?_without_notify_email:1}
+%define with_notify_nagios 0%{!?_without_notify_nagios:1}
 %define with_ntpd 0%{!?_without_ntpd:1}
 %define with_numa 0%{!?_without_numa:1}
 %define with_nut 0%{!?_without_nut:1}
@@ -1233,6 +1234,12 @@ Collectd utilities
 %define _with_notify_email --disable-notify_email
 %endif
 
+%if %{with_notify_nagios}
+%define _with_notify_nagios --enable-notify_nagios
+%else
+%define _with_notify_nagios --disable-notify_nagios
+%endif
+
 %if %{with_ntpd}
 %define _with_ntpd --enable-ntpd
 %else
@@ -1711,6 +1718,7 @@ Collectd utilities
        %{?_with_memory} \
        %{?_with_network} \
        %{?_with_nfs} \
+       %{?_with_notify_nagios} \
        %{?_with_ntpd} \
        %{?_with_numa} \
        %{?_with_olsrd} \
@@ -1977,6 +1985,9 @@ fi
 %if %{with_nfs}
 %{_libdir}/%{name}/nfs.so
 %endif
+%if %{with_notify_nagios}
+%{_libdir}/%{name}/notify_nagios.so
+%endif
 %if %{with_ntpd}
 %{_libdir}/%{name}/ntpd.so
 %endif
@@ -2370,7 +2381,7 @@ fi
 %changelog
 #* TODO: next feature release changelog
 #- New upstream version
-#- New plugins enabled by default: mqtt
+#- New plugins enabled by default: mqtt, notify_nagios
 #- New plugins disabled by default: zone
 #
 * Wed May 27 2015 Marc Fournier <marc.fournier@camptocamp.com> 5.5.0-1
index d3b1acd..6267a31 100644 (file)
@@ -28,7 +28,7 @@ liblatency_la_SOURCES = utils_latency.c utils_latency.h
 check_PROGRAMS += test_utils_latency
 TESTS += test_utils_latency
 test_utils_latency_SOURCES = utils_latency_test.c testing.h
-test_utils_latency_LDADD = liblatency.la daemon/libcommon.la daemon/libplugin_mock.la -lm
+test_utils_latency_LDADD = liblatency.la daemon/libplugin_mock.la -lm
 
 noinst_LTLIBRARIES += liblookup.la
 liblookup_la_SOURCES = utils_vl_lookup.c utils_vl_lookup.h
@@ -36,7 +36,7 @@ liblookup_la_LIBADD = daemon/libavltree.la
 check_PROGRAMS += test_utils_vl_lookup
 TESTS += test_utils_vl_lookup
 test_utils_vl_lookup_SOURCES = utils_vl_lookup_test.c testing.h
-test_utils_vl_lookup_LDADD = liblookup.la daemon/libcommon.la daemon/libplugin_mock.la
+test_utils_vl_lookup_LDADD = liblookup.la daemon/libplugin_mock.la
 if BUILD_WITH_LIBKSTAT
 test_utils_vl_lookup_LDADD += -lkstat
 endif
@@ -46,7 +46,7 @@ libmount_la_SOURCES = utils_mount.c utils_mount.h
 check_PROGRAMS += test_utils_mount
 TESTS += test_utils_mount
 test_utils_mount_SOURCES = utils_mount_test.c testing.h
-test_utils_mount_LDADD = libmount.la daemon/libcommon.la daemon/libplugin_mock.la
+test_utils_mount_LDADD = libmount.la daemon/libplugin_mock.la
 if BUILD_WITH_LIBKSTAT
 test_utils_mount_LDADD += -lkstat
 endif
index aade08a..afa7e48 100644 (file)
@@ -620,6 +620,11 @@ sets the type instance instead. I<(optional)>
 Works like the option of the same name directly beneath the I<MBean> block, but
 sets the type instance instead. I<(optional)>
 
+=item B<PluginName> I<name>
+
+When set, overrides the default setting for the I<plugin> field
+(C<GenericJMX>).
+
 =item B<Table> B<true>|B<false>
 
 Set this to true if the returned attribute is a I<composite type>. If set to
index 1c14759..eb23023 100644 (file)
 #  DeleteTimers   false
 #  DeleteGauges   false
 #  DeleteSets     false
+#  CounterSum     false
 #  TimerPercentile 90.0
 #  TimerPercentile 95.0
 #  TimerPercentile 99.0
index 84c7772..7c5a3c0 100644 (file)
@@ -6232,6 +6232,12 @@ rate of counters and size of sets will be zero, timers report C<NaN> and gauges
 are unchanged. If set to B<True>, the such metrics are not dispatched and
 removed from the internal cache.
 
+=item B<CounterSum> B<false>|B<true>
+
+When enabled, create a C<count> metric which reports the change since the last
+read. This option primarily exists for compatibility with the I<statsd>
+impelemtation by Etsy.
+
 =item B<TimerPercentile> I<Percent>
 
 Calculate and dispatch the configured percentile, i.e. compute the latency, so
index 3d50029..cdd927b 100644 (file)
@@ -48,8 +48,10 @@ libheap_la_SOURCES = utils_heap.c utils_heap.h
 
 libmetadata_la_SOURCES = meta_data.c meta_data.h
 
-libplugin_mock_la_SOURCES = plugin_mock.c utils_cache_mock.c utils_time_mock.c
-libplugin_mock_la_LIBADD = $(COMMON_LIBS)
+libplugin_mock_la_SOURCES = plugin_mock.c utils_cache_mock.c \
+                           utils_time.c utils_time.h
+libplugin_mock_la_CPPFLAGS = $(AM_CPPFLAGS) -DMOCK_TIME
+libplugin_mock_la_LIBADD = $(COMMON_LIBS) libcommon.la
 
 collectd_SOURCES = collectd.c collectd.h \
                   configfile.c configfile.h \
@@ -94,7 +96,7 @@ check_PROGRAMS = test_common test_meta_data test_utils_avltree test_utils_heap t
 TESTS          = test_common test_meta_data test_utils_avltree test_utils_heap test_utils_time test_utils_subst
 
 test_common_SOURCES = common_test.c ../testing.h
-test_common_LDADD = libcommon.la libplugin_mock.la
+test_common_LDADD = libplugin_mock.la
 
 test_meta_data_SOURCES = meta_data_test.c ../testing.h
 test_meta_data_LDADD = libmetadata.la libplugin_mock.la
@@ -109,4 +111,4 @@ test_utils_time_SOURCES = utils_time_test.c ../testing.h
 
 test_utils_subst_SOURCES = utils_subst_test.c ../testing.h \
                           utils_subst.c utils_subst.h
-test_utils_subst_LDADD = libcommon.la libplugin_mock.la
+test_utils_subst_LDADD = libplugin_mock.la
index fe6cc2b..0919bdf 100644 (file)
@@ -483,7 +483,9 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
        {
                read_func_t *rf;
                plugin_ctx_t old_ctx;
+               cdtime_t start;
                cdtime_t now;
+               cdtime_t elapsed;
                int status;
                int rf_type;
                int rc;
@@ -562,6 +564,8 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
 
                DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
 
+               start = cdtime ();
+
                old_ctx = plugin_set_ctx (rf->rf_ctx);
 
                if (rf_type == RF_SIMPLE)
@@ -605,8 +609,22 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                /* update the ``next read due'' field */
                now = cdtime ();
 
+               /* calculate the time spent in the read function */
+               elapsed = (now - start);
+
+               if (elapsed > rf->rf_effective_interval)
+                       WARNING ("plugin_read_thread: read-function of the `%s' plugin took %.3f "
+                               "seconds, which is above its read interval (%.3f seconds). You might "
+                               "want to adjust the `Interval' or `ReadThreads' settings.",
+                               rf->rf_name, CDTIME_T_TO_DOUBLE(elapsed),
+                               CDTIME_T_TO_DOUBLE(rf->rf_effective_interval));
+
+               DEBUG ("plugin_read_thread: read-function of the `%s' plugin took "
+                               "%.6f seconds.",
+                               rf->rf_name, CDTIME_T_TO_DOUBLE(elapsed));
+
                DEBUG ("plugin_read_thread: Effective interval of the "
-                               "%s plugin is %.3f seconds.",
+                               "`%s' plugin is %.3f seconds.",
                                rf->rf_name,
                                CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
 
@@ -623,7 +641,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                        rf->rf_next_read = now;
                }
 
-               DEBUG ("plugin_read_thread: Next read of the %s plugin at %.3f.",
+               DEBUG ("plugin_read_thread: Next read of the `%s' plugin at %.3f.",
                                rf->rf_name,
                                CDTIME_T_TO_DOUBLE (rf->rf_next_read));
 
index 5d905d9..7f482da 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_time.c
- * Copyright (C) 2010       Florian octo Forster
+ * Copyright (C) 2010-2015  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"),
@@ -21,7 +21,7 @@
  * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <ff at octo.it>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
 #include "plugin.h"
 #include "common.h"
 
-#if HAVE_CLOCK_GETTIME
+#ifndef DEFAULT_MOCK_TIME
+# define DEFAULT_MOCK_TIME 1542455354518929408ULL
+#endif
+
+#ifdef MOCK_TIME
+cdtime_t cdtime_mock = (cdtime_t) MOCK_TIME;
+
+cdtime_t cdtime (void)
+{
+  return cdtime_mock;
+}
+#else /* !MOCK_TIME */
+# if HAVE_CLOCK_GETTIME
 cdtime_t cdtime (void) /* {{{ */
 {
   int status;
@@ -46,7 +58,7 @@ cdtime_t cdtime (void) /* {{{ */
 
   return (TIMESPEC_TO_CDTIME_T (&ts));
 } /* }}} cdtime_t cdtime */
-#else
+# else /* !HAVE_CLOCK_GETTIME */
 /* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */
 cdtime_t cdtime (void) /* {{{ */
 {
@@ -64,43 +76,88 @@ cdtime_t cdtime (void) /* {{{ */
 
   return (TIMEVAL_TO_CDTIME_T (&tv));
 } /* }}} cdtime_t cdtime */
+# endif
 #endif
 
-size_t cdtime_to_iso8601 (char *s, size_t max, cdtime_t t) /* {{{ */
+/* format_zone reads time zone information from "extern long timezone", exported
+ * by <time.h>, and formats it according to RFC 3339. This differs from
+ * strftime()'s "%z" format by including a colon between hour and minute. */
+static void format_zone (char *buffer, size_t buffer_size) /* {{{ */
+{
+  _Bool east = 0;
+  long hours;
+  long minutes;
+
+  minutes = timezone / 60;
+  if (minutes == 0) {
+    sstrncpy (buffer, "Z", buffer_size);
+    return;
+  }
+
+  if (minutes < 0)
+  {
+    east = 1;
+    minutes = minutes * (-1);
+  }
+
+  hours = minutes / 60;
+  minutes = minutes % 60;
+
+  ssnprintf (buffer, buffer_size, "%s%02ld:%02ld",
+             (east ? "+" : "-"), hours, minutes);
+} /* }}} int format_zone */
+
+static int format_rfc3339 (char *buffer, size_t buffer_size, cdtime_t t, _Bool print_nano) /* {{{ */
 {
   struct timespec t_spec;
   struct tm t_tm;
-
+  char base[20]; /* 2006-01-02T15:04:05 */
+  char nano[11]; /* .999999999 */
+  char zone[7];  /* +00:00 */
+  char *fields[] = {base, nano, zone};
   size_t len;
 
   CDTIME_T_TO_TIMESPEC (t, &t_spec);
   NORMALIZE_TIMESPEC (t_spec);
 
-  if (localtime_r ((time_t *)&t_spec.tv_sec, &t_tm) == NULL) {
+  if (localtime_r (&t_spec.tv_sec, &t_tm) == NULL) {
     char errbuf[1024];
-    ERROR ("cdtime_to_iso8601: localtime_r failed: %s",
-        sstrerror (errno, errbuf, sizeof (errbuf)));
-    return (0);
+    int status = errno;
+    ERROR ("format_rfc3339: localtime_r failed: %s",
+        sstrerror (status, errbuf, sizeof (errbuf)));
+    return (status);
   }
 
-  len = strftime (s, max, "%Y-%m-%dT%H:%M:%S", &t_tm);
+  len = strftime (base, sizeof (base), "%Y-%m-%dT%H:%M:%S", &t_tm);
   if (len == 0)
-    return 0;
+    return ENOMEM;
 
-  if (max - len > 2) {
-    int n = snprintf (s + len, max - len, ".%09i", (int)t_spec.tv_nsec);
-    len += (n < 0) ? 0
-      : (((size_t) n) < (max - len)) ? ((size_t) n)
-      : (max - len);
-  }
+  if (print_nano)
+    ssnprintf (nano, sizeof (nano), ".%09ld", (long) t_spec.tv_nsec);
+  else
+    sstrncpy (nano, "", sizeof (nano));
 
-  if (max - len > 3) {
-    size_t n = strftime (s + len, max - len, "%z", &t_tm);
-    len += (n < max - len) ? n : max - len;
-  }
+  format_zone (zone, sizeof (zone));
+
+  if (strjoin (buffer, buffer_size, fields, STATIC_ARRAY_SIZE (fields), "") < 0)
+    return ENOMEM;
+  return 0;
+} /* }}} int cdtime_to_rfc3339nano */
+
+int rfc3339 (char *buffer, size_t buffer_size, cdtime_t t) /* {{{ */
+{
+  if (buffer_size < RFC3339_SIZE)
+    return ENOMEM;
+
+  return format_rfc3339 (buffer, buffer_size, t, 0);
+} /* }}} size_t cdtime_to_rfc3339 */
+
+int rfc3339nano (char *buffer, size_t buffer_size, cdtime_t t) /* {{{ */
+{
+  if (buffer_size < RFC3339NANO_SIZE)
+    return ENOMEM;
 
-  s[max - 1] = '\0';
-  return len;
-} /* }}} size_t cdtime_to_iso8601 */
+  return format_rfc3339 (buffer, buffer_size, t, 1);
+} /* }}} size_t cdtime_to_rfc3339nano */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 6566a73..9e98040 100644 (file)
 
 #include "collectd.h"
 
+#ifdef TESTING_H
+/* cdtime_mock is the time returned by cdtime() when build with
+ * -DMOCK_TIME */
+extern cdtime_t cdtime_mock;
+#endif
+
 /*
  * "cdtime_t" is a 64bit unsigned integer. The time is stored at a 2^-30 second
  * resolution, i.e. the most significant 34 bit are used to store the time in
 
 cdtime_t cdtime (void);
 
-/* format a cdtime_t value in ISO 8601 format:
- * returns the number of characters written to the string (not including the
- * terminating null byte or 0 on error; the function ensures that the string
- * is null terminated */
-size_t cdtime_to_iso8601 (char *s, size_t max, cdtime_t t);
+#define RFC3339_SIZE     26
+#define RFC3339NANO_SIZE 36
+
+/* rfc3339 formats a cdtime_t time in RFC 3339 format with second precision. */
+int rfc3339 (char *buffer, size_t buffer_size, cdtime_t t);
+
+/* rfc3339nano formats a cdtime_t time in RFC 3339 format with nanosecond
+ * precision. */
+int rfc3339nano (char *buffer, size_t buffer_size, cdtime_t t);
 
 #endif /* UTILS_TIME_H */
 /* vim: set sw=2 sts=2 et : */
diff --git a/src/daemon/utils_time_mock.c b/src/daemon/utils_time_mock.c
deleted file mode 100644 (file)
index 217515e..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * collectd - src/tests/mock/utils_time.c
- * Copyright (C) 2013       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>
- */
-
-#include "utils_time.h"
-
-cdtime_t cdtime (void)
-{
-  return ((cdtime_t) 1542455354518929408ULL);
-}
-
index 663a82d..93c8f1b 100644 (file)
@@ -697,6 +697,14 @@ static int openvpn_config (const char *key, const char *value)
 
                /* create a new vpn element since file, version and name are ok */
                temp = (vpn_status_t *) malloc (sizeof (vpn_status_t));
+               if (temp == NULL)
+               {
+                       char errbuf[1024];
+                       ERROR ("openvpn plugin: malloc failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       sfree (status_file);
+                       return (1);
+               }
                temp->file = status_file;
                temp->version = status_version;
                temp->name = status_name;
@@ -705,7 +713,7 @@ static int openvpn_config (const char *key, const char *value)
                if (vpn_list == NULL)
                {
                        char errbuf[1024];
-                       ERROR ("openvpn plugin: malloc failed: %s",
+                       ERROR ("openvpn plugin: realloc failed: %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
 
                        sfree (temp->file);
index bef3490..e68fd32 100644 (file)
@@ -826,7 +826,7 @@ static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
 {
        c_psql_database_t *db;
 
-       char time_str[32];
+       char time_str[RFC3339NANO_SIZE];
        char values_name_str[1024];
        char values_type_str[1024];
        char values_str[1024];
@@ -845,8 +845,8 @@ static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
        assert (db->database != NULL);
        assert (db->writers != NULL);
 
-       if (cdtime_to_iso8601 (time_str, sizeof (time_str), vl->time) == 0) {
-               log_err ("c_psql_write: Failed to convert time to ISO 8601 format");
+       if (rfc3339nano (time_str, sizeof (time_str), vl->time) != 0) {
+               log_err ("c_psql_write: Failed to convert time to RFC 3339 format");
                return -1;
        }
 
index 162426a..0717365 100644 (file)
@@ -1106,6 +1106,14 @@ static int csnmp_strvbcopy (char *dst, /* {{{ */
     src = (char *) vb->val.string;
   else if (vb->type == ASN_BIT_STR)
     src = (char *) vb->val.bitstring;
+  else if (vb->type == ASN_IPADDRESS)
+  {
+    return ssnprintf (dst, dst_size, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"",
+          (uint8_t) vb->val.string[0],
+          (uint8_t) vb->val.string[1],
+          (uint8_t) vb->val.string[2],
+          (uint8_t) vb->val.string[3]);
+  }
   else
   {
     dst[0] = 0;
@@ -1171,7 +1179,7 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   }
 
   /* Get instance name */
-  if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
+  if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR) || (vb->type == ASN_IPADDRESS))
   {
     char *ptr;
 
@@ -1783,8 +1791,6 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 static int csnmp_read_host (user_data_t *ud)
 {
   host_definition_t *host;
-  cdtime_t time_start;
-  cdtime_t time_end;
   int status;
   int success;
   int i;
@@ -1794,8 +1800,6 @@ static int csnmp_read_host (user_data_t *ud)
   if (host->interval == 0)
     host->interval = plugin_get_interval ();
 
-  time_start = cdtime ();
-
   if (host->sess_handle == NULL)
     csnmp_host_open_session (host);
 
@@ -1816,16 +1820,6 @@ static int csnmp_read_host (user_data_t *ud)
       success++;
   }
 
-  time_end = cdtime ();
-  if ((time_end - time_start) > host->interval)
-  {
-    WARNING ("snmp plugin: Host `%s' should be queried every %.3f "
-        "seconds, but reading all values takes %.3f seconds.",
-        host->name,
-        CDTIME_T_TO_DOUBLE (host->interval),
-        CDTIME_T_TO_DOUBLE (time_end - time_start));
-  }
-
   if (success == 0)
     return (-1);
 
index 8047b1a..ea4123c 100644 (file)
@@ -64,6 +64,7 @@ struct statsd_metric_s
 {
   metric_type_t type;
   double value;
+  derive_t counter;
   latency_counter_t *latency;
   c_avl_tree_t *set;
   unsigned long updates_num;
@@ -88,6 +89,7 @@ static _Bool conf_delete_sets     = 0;
 static double *conf_timer_percentile = NULL;
 static size_t  conf_timer_percentile_num = 0;
 
+static _Bool conf_counter_sum     = 0;
 static _Bool conf_timer_lower     = 0;
 static _Bool conf_timer_upper     = 0;
 static _Bool conf_timer_sum       = 0;
@@ -261,6 +263,8 @@ static int statsd_handle_counter (char const *name, /* {{{ */
   if (status != 0)
     return (status);
 
+  /* Changes to the counter are added to (statsd_metric_t*)->value. ->counter is
+   * only updated in statsd_metric_submit_unsafe(). */
   return (statsd_metric_add (name, (double) (value.gauge / scale.gauge),
         STATSD_COUNTER));
 } /* }}} int statsd_handle_counter */
@@ -684,6 +688,8 @@ static int statsd_config (oconfig_item_t *ci) /* {{{ */
       cf_util_get_boolean (child, &conf_delete_gauges);
     else if (strcasecmp ("DeleteSets", child->key) == 0)
       cf_util_get_boolean (child, &conf_delete_sets);
+    else if (strcasecmp ("CounterSum", child->key) == 0)
+      cf_util_get_boolean (child, &conf_counter_sum);
     else if (strcasecmp ("TimerLower", child->key) == 0)
       cf_util_get_boolean (child, &conf_timer_lower);
     else if (strcasecmp ("TimerUpper", child->key) == 0)
@@ -754,8 +760,7 @@ static int statsd_metric_clear_set_unsafe (statsd_metric_t *metric) /* {{{ */
 } /* }}} int statsd_metric_clear_set_unsafe */
 
 /* Must hold metrics_lock when calling this function. */
-static int statsd_metric_submit_unsafe (char const *name, /* {{{ */
-    statsd_metric_t const *metric)
+static int statsd_metric_submit_unsafe (char const *name, statsd_metric_t *metric) /* {{{ */
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
@@ -851,17 +856,28 @@ static int statsd_metric_submit_unsafe (char const *name, /* {{{ */
       values[0].gauge = (gauge_t) c_avl_size (metric->set);
   }
   else { /* STATSD_COUNTER */
-      /*
-       * Expand a single value to two metrics:
-       *
-       * - The absolute counter, as a gauge
-       * - A derived rate for this counter
-       */
-      values[0].derive = (derive_t) metric->value;
-      plugin_dispatch_values(&vl);
-
-      sstrncpy(vl.type, "gauge", sizeof (vl.type));
-      values[0].gauge = (gauge_t) metric->value;
+    gauge_t delta = nearbyint (metric->value);
+
+    /* Etsy's statsd writes counters as two metrics: a rate and the change since
+     * the last write. Since collectd does not reset its DERIVE metrics to zero,
+     * this makes little sense, but we're dispatching a "count" metric here
+     * anyway - if requested by the user - for compatibility reasons. */
+    if (conf_counter_sum)
+    {
+      sstrncpy (vl.type, "count", sizeof (vl.type));
+      values[0].gauge = delta;
+      plugin_dispatch_values (&vl);
+
+      /* restore vl.type */
+      sstrncpy (vl.type, "derive", sizeof (vl.type));
+    }
+
+    /* Rather than resetting value to zero, subtract delta so we correctly keep
+     * track of residuals. */
+    metric->value   -= delta;
+    metric->counter += (derive_t) delta;
+
+    values[0].derive = metric->counter;
   }
 
   return (plugin_dispatch_values (&vl));
index 0c415e4..c0a9e88 100644 (file)
@@ -24,6 +24,9 @@
  *   Florian octo Forster <octo at collectd.org>
  */
 
+#ifndef TESTING_H
+#define TESTING_H 1
+
 #include <inttypes.h>
 
 static int fail_count__ = 0;
@@ -109,3 +112,5 @@ static int check_count__ = 0;
   status_ = (long) (expr); \
   OK1(status_ == 0L, #expr); \
 } while (0)
+
+#endif /* TESTING_H */
index c6c4df0..65562d3 100644 (file)
@@ -588,6 +588,7 @@ static int varnish_read (user_data_t *ud) /* {{{ */
 {
        struct VSM_data *vd;
        const c_varnish_stats_t *stats;
+       _Bool ok;
 
        user_config_t *conf;
 
@@ -617,10 +618,11 @@ static int varnish_read (user_data_t *ud) /* {{{ */
        }
 
 #if HAVE_VARNISH_V3
-       if (VSC_Open (vd, /* diag = */ 1))
+       ok = (VSC_Open (vd, /* diag = */ 1) == 0);
 #else /* if HAVE_VARNISH_V4 */
-       if (VSM_Open (vd))
+       ok = (VSM_Open (vd) == 0);
 #endif
+       if (!ok)
        {
                VSM_Delete (vd);
                ERROR ("varnish plugin: Unable to open connection.");
index 775e2e0..a5977ab 100644 (file)
@@ -44,7 +44,7 @@ struct kafka_topic_context {
 #define KAFKA_FORMAT_JSON        0
 #define KAFKA_FORMAT_COMMAND     1
 #define KAFKA_FORMAT_GRAPHITE    2
-    uint8_t                     format;
+    uint8_t                      format;
     unsigned int                 graphite_flags;
     _Bool                        store_rates;
     rd_kafka_topic_conf_t       *conf;
@@ -52,12 +52,12 @@ struct kafka_topic_context {
     rd_kafka_conf_t             *kafka_conf;
     rd_kafka_t                  *kafka;
     int                          has_key;
-    uint32_t                    key;
+    uint32_t                     key;
     char                        *prefix;
     char                        *postfix;
     char                         escape_char;
     char                        *topic_name;
-    pthread_mutex_t            lock;
+    pthread_mutex_t              lock;
 };
 
 static int kafka_handle(struct kafka_topic_context *);
@@ -81,7 +81,7 @@ static int32_t kafka_partition(const rd_kafka_topic_t *rkt,
 {
     uint32_t key = *((uint32_t *)keydata );
     uint32_t target = key % partition_cnt;
-    int32_t   i = partition_cnt;
+    int32_t  i = partition_cnt;
 
     while (--i > 0 && !rd_kafka_topic_partition_available(rkt, target)) {
         target = (target + 1) % partition_cnt;
@@ -106,37 +106,37 @@ static int kafka_handle(struct kafka_topic_context *ctx) /* {{{ */
 
         if ((ctx->kafka = rd_kafka_new(RD_KAFKA_PRODUCER, conf,
                                     errbuf, sizeof(errbuf))) == NULL) {
-               ERROR("write_kafka plugin: cannot create kafka handle.");
-               return 1;
+            ERROR("write_kafka plugin: cannot create kafka handle.");
+            return 1;
         }
 
-       rd_kafka_conf_destroy(ctx->kafka_conf);
-       ctx->kafka_conf = NULL;
+        rd_kafka_conf_destroy(ctx->kafka_conf);
+        ctx->kafka_conf = NULL;
 
-       INFO ("write_kafka plugin: created KAFKA handle : %s", rd_kafka_name(ctx->kafka));
+        INFO ("write_kafka plugin: created KAFKA handle : %s", rd_kafka_name(ctx->kafka));
 
 #ifdef HAVE_LIBRDKAFKA_LOGGER
-       rd_kafka_set_logger(ctx->kafka, kafka_log);
+        rd_kafka_set_logger(ctx->kafka, kafka_log);
 #endif
     }
 
     if (ctx->topic == NULL ) {
-       if ((topic_conf = rd_kafka_topic_conf_dup(ctx->conf)) == NULL) {
+        if ((topic_conf = rd_kafka_topic_conf_dup(ctx->conf)) == NULL) {
             ERROR("write_kafka plugin: cannot duplicate kafka topic config");
             return 1;
-       }
+        }
 
-       if ((ctx->topic = rd_kafka_topic_new(ctx->kafka, ctx->topic_name,
-                                                       topic_conf)) == NULL) {
-               ERROR("write_kafka plugin: cannot create topic : %s\n", 
-                       rd_kafka_err2str(rd_kafka_errno2err(errno)));
-               return errno;
-       }
+        if ((ctx->topic = rd_kafka_topic_new(ctx->kafka, ctx->topic_name,
+                                            topic_conf)) == NULL) {
+            ERROR("write_kafka plugin: cannot create topic : %s\n",
+            rd_kafka_err2str(rd_kafka_errno2err(errno)));
+            return errno;
+        }
 
-       rd_kafka_topic_conf_destroy(ctx->conf);
-       ctx->conf = NULL;
+        rd_kafka_topic_conf_destroy(ctx->conf);
+        ctx->conf = NULL;
 
-       INFO ("write_kafka plugin: handle created for topic : %s", rd_kafka_topic_name(ctx->topic));
+        INFO ("write_kafka plugin: handle created for topic : %s", rd_kafka_topic_name(ctx->topic));
     }
 
     return(0);
@@ -144,16 +144,16 @@ static int kafka_handle(struct kafka_topic_context *ctx) /* {{{ */
 } /* }}} int kafka_handle */
 
 static int kafka_write(const data_set_t *ds, /* {{{ */
-             const value_list_t *vl,
-             user_data_t *ud)
+          const value_list_t *vl,
+          user_data_t *ud)
 {
-       int                      status = 0;
-    uint32_t    key;
-    char         buffer[8192];
-    size_t bfree = sizeof(buffer);
-    size_t bfill = 0;
-    size_t blen = 0;
-       struct kafka_topic_context      *ctx = ud->data;
+    int      status = 0;
+    uint32_t key;
+    char     buffer[8192];
+    size_t   bfree = sizeof(buffer);
+    size_t   bfill = 0;
+    size_t   blen = 0;
+    struct   kafka_topic_context  *ctx = ud->data;
 
     if ((ds == NULL) || (vl == NULL) || (ctx == NULL))
         return EINVAL;
@@ -177,7 +177,6 @@ static int kafka_write(const data_set_t *ds, /* {{{ */
         blen = strlen(buffer);
         break;
     case KAFKA_FORMAT_JSON:
-
         format_json_initialize(buffer, &bfill, &bfree);
         format_json_value_list(buffer, &bfill, &bfree, ds, vl,
                                ctx->store_rates);
@@ -212,15 +211,15 @@ static int kafka_write(const data_set_t *ds, /* {{{ */
                      RD_KAFKA_MSG_F_COPY, buffer, blen,
                      &key, sizeof(key), NULL);
 
-       return status;
+    return status;
 } /* }}} int kafka_write */
 
 static void kafka_topic_context_free(void *p) /* {{{ */
 {
-       struct kafka_topic_context *ctx = p;
+    struct kafka_topic_context *ctx = p;
 
-       if (ctx == NULL)
-               return;
+    if (ctx == NULL)
+        return;
 
     if (ctx->topic_name != NULL)
         sfree(ctx->topic_name);
@@ -246,13 +245,13 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{
     char                         callback_name[DATA_MAX_NAME_LEN];
     char                         errbuf[1024];
     user_data_t                  ud;
-       oconfig_item_t              *child;
+    oconfig_item_t              *child;
     rd_kafka_conf_res_t          ret;
 
-       if ((tctx = calloc(1, sizeof (*tctx))) == NULL) {
-               ERROR ("write_kafka plugin: calloc failed.");
+    if ((tctx = calloc(1, sizeof (*tctx))) == NULL) {
+        ERROR ("write_kafka plugin: calloc failed.");
         return;
-       }
+    }
 
     tctx->escape_char = '.';
     tctx->store_rates = 1;
@@ -290,33 +289,33 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{
         goto errout;
     }
 
-       for (i = 0; i < ci->children_num; i++) {
-               /*
-                * The code here could be simplified but makes room
-                * for easy adding of new options later on.
-                */
-               child = &ci->children[i];
-               status = 0;
-
-               if (strcasecmp ("Property", child->key) == 0) {
-                       if (child->values_num != 2) {
-                               WARNING("kafka properties need both a key and a value.");
+    for (i = 0; i < ci->children_num; i++) {
+        /*
+         * The code here could be simplified but makes room
+         * for easy adding of new options later on.
+         */
+        child = &ci->children[i];
+        status = 0;
+
+        if (strcasecmp ("Property", child->key) == 0) {
+            if (child->values_num != 2) {
+                WARNING("kafka properties need both a key and a value.");
                 goto errout;
-                       }
-                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
-                           child->values[1].type != OCONFIG_TYPE_STRING) {
-                               WARNING("kafka properties needs string arguments.");
+            }
+            if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                child->values[1].type != OCONFIG_TYPE_STRING) {
+                WARNING("kafka properties needs string arguments.");
                 goto errout;
-                       }
+            }
             key = child->values[0].value.string;
             val = child->values[1].value.string;
             ret = rd_kafka_topic_conf_set(tctx->conf,key, val,
                                           errbuf, sizeof(errbuf));
             if (ret != RD_KAFKA_CONF_OK) {
-                               WARNING("cannot set kafka topic property %s to %s: %s.",
+                WARNING("cannot set kafka topic property %s to %s: %s.",
                         key, val, errbuf);
                 goto errout;
-                       }
+            }
 
         } else if (strcasecmp ("Key", child->key) == 0)  {
             char *tmp_buf = NULL;
@@ -397,11 +396,11 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{
     ud.data = tctx;
     ud.free_func = kafka_topic_context_free;
 
-       status = plugin_register_write (callback_name, kafka_write, &ud);
-       if (status != 0) {
-               WARNING ("write_kafka plugin: plugin_register_write (\"%s\") "
-                               "failed with status %i.",
-                               callback_name, status);
+    status = plugin_register_write (callback_name, kafka_write, &ud);
+    if (status != 0) {
+        WARNING ("write_kafka plugin: plugin_register_write (\"%s\") "
+                "failed with status %i.",
+                callback_name, status);
         goto errout;
     }
 
@@ -414,14 +413,14 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{
     if (tctx->conf != NULL)
         rd_kafka_topic_conf_destroy(tctx->conf);
     if (tctx->kafka_conf != NULL)
-               rd_kafka_conf_destroy(tctx->kafka_conf);
+        rd_kafka_conf_destroy(tctx->kafka_conf);
     sfree(tctx);
 } /* }}} int kafka_config_topic */
 
 static int kafka_config(oconfig_item_t *ci) /* {{{ */
 {
-       int                          i;
-       oconfig_item_t              *child;
+    int                          i;
+    oconfig_item_t              *child;
     rd_kafka_conf_t             *conf;
     rd_kafka_conf_res_t          ret;
     char                         errbuf[1024];
@@ -430,49 +429,49 @@ static int kafka_config(oconfig_item_t *ci) /* {{{ */
         WARNING("cannot allocate kafka configuration.");
         return -1;
     }
-       for (i = 0; i < ci->children_num; i++)  {
-               child = &ci->children[i];
+    for (i = 0; i < ci->children_num; i++)  {
+        child = &ci->children[i];
 
-               if (strcasecmp("Topic", child->key) == 0) {
-                       kafka_config_topic (conf, child);
-               } else if (strcasecmp(child->key, "Property") == 0) {
-                       char *key = NULL;
-                       char *val = NULL;
+        if (strcasecmp("Topic", child->key) == 0) {
+            kafka_config_topic (conf, child);
+        } else if (strcasecmp(child->key, "Property") == 0) {
+            char *key = NULL;
+            char *val = NULL;
 
-                       if (child->values_num != 2) {
-                               WARNING("kafka properties need both a key and a value.");
+            if (child->values_num != 2) {
+                WARNING("kafka properties need both a key and a value.");
                 goto errout;
-                       }
-                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
-                           child->values[1].type != OCONFIG_TYPE_STRING) {
-                               WARNING("kafka properties needs string arguments.");
+            }
+            if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                child->values[1].type != OCONFIG_TYPE_STRING) {
+                WARNING("kafka properties needs string arguments.");
                 goto errout;
-                       }
-                       if ((key = strdup(child->values[0].value.string)) == NULL) {
-                               WARNING("cannot allocate memory for attribute key.");
+            }
+            if ((key = strdup(child->values[0].value.string)) == NULL) {
+                WARNING("cannot allocate memory for attribute key.");
                 goto errout;
-                       }
-                       if ((val = strdup(child->values[1].value.string)) == NULL) {
-                               WARNING("cannot allocate memory for attribute value.");
+            }
+            if ((val = strdup(child->values[1].value.string)) == NULL) {
+                WARNING("cannot allocate memory for attribute value.");
                 goto errout;
-                       }
+            }
             ret = rd_kafka_conf_set(conf, key, val, errbuf, sizeof(errbuf));
             if (ret != RD_KAFKA_CONF_OK) {
                 WARNING("cannot set kafka property %s to %s: %s",
                         key, val, errbuf);
                 goto errout;
             }
-                       sfree(key);
-                       sfree(val);
-               } else {
-                       WARNING ("write_kafka plugin: Ignoring unknown "
-                                "configuration option \"%s\" at top level.",
-                                child->key);
-               }
-       }
+            sfree(key);
+            sfree(val);
+        } else {
+            WARNING ("write_kafka plugin: Ignoring unknown "
+                 "configuration option \"%s\" at top level.",
+                 child->key);
+        }
+    }
     if (conf != NULL)
         rd_kafka_conf_destroy(conf);
-       return (0);
+    return (0);
  errout:
     if (conf != NULL)
         rd_kafka_conf_destroy(conf);
@@ -481,7 +480,6 @@ static int kafka_config(oconfig_item_t *ci) /* {{{ */
 
 void module_register(void)
 {
-       plugin_register_complex_config ("write_kafka", kafka_config);
+    plugin_register_complex_config ("write_kafka", kafka_config);
 }
 
-/* vim: set sw=8 sts=8 ts=8 noet : */