Merge branch 'collectd-5.3' into collectd-5.4
authorMarc Fournier <marc.fournier@camptocamp.com>
Mon, 24 Nov 2014 08:23:18 +0000 (09:23 +0100)
committerMarc Fournier <marc.fournier@camptocamp.com>
Mon, 24 Nov 2014 08:23:18 +0000 (09:23 +0100)
Conflicts:
src/write_graphite.c

1  2 
src/Makefile.am
src/collectd.conf.pod
src/plugin.c
src/write_graphite.c

diff --combined src/Makefile.am
@@@ -143,7 -143,7 +143,7 @@@ pkglib_LTLIBRARIES += aggregation.l
  aggregation_la_SOURCES = aggregation.c \
                           utils_vl_lookup.c utils_vl_lookup.h
  aggregation_la_LDFLAGS = -module -avoid-version
- aggregation_la_LIBADD =
+ aggregation_la_LIBADD = -lm
  collectd_LDADD += "-dlopen" aggregation.la
  collectd_DEPENDENCIES += aggregation.la
  endif
@@@ -196,16 -196,6 +196,16 @@@ collectd_LDADD += "-dlopen" apple_senso
  collectd_DEPENDENCIES += apple_sensors.la
  endif
  
 +if BUILD_PLUGIN_AQUAERO
 +pkglib_LTLIBRARIES += aquaero.la
 +aquaero_la_SOURCES = aquaero.c
 +aquaero_la_LDFLAGS = -module -avoid-version
 +aquaero_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBAQUAERO5_CFLAGS)
 +aquaero_la_LIBADD = $(BUILD_WITH_LIBAQUAERO5_LDFLAGS) -laquaero5
 +collectd_LDADD += "-dlopen" aquaero.la
 +collectd_DEPENDENCIES += aquaero.la
 +endif
 +
  if BUILD_PLUGIN_ASCENT
  pkglib_LTLIBRARIES += ascent.la
  ascent_la_SOURCES = ascent.c
@@@ -240,14 -230,6 +240,14 @@@ collectd_LDADD += "-dlopen" bind.l
  collectd_DEPENDENCIES += bind.la
  endif
  
 +if BUILD_PLUGIN_CGROUPS
 +pkglib_LTLIBRARIES += cgroups.la
 +cgroups_la_SOURCES = cgroups.c utils_mount.c utils_mount.h
 +cgroups_la_LDFLAGS = -module -avoid-version
 +collectd_LDADD += "-dlopen" cgroups.la
 +collectd_DEPENDENCIES += cgroups.la
 +endif
 +
  if BUILD_PLUGIN_CONNTRACK
  pkglib_LTLIBRARIES += conntrack.la
  conntrack_la_SOURCES = conntrack.c
@@@ -586,15 -568,6 +586,15 @@@ collectd_DEPENDENCIES += lpar.l
  lpar_la_LIBADD = -lperfstat
  endif
  
 +if BUILD_PLUGIN_LVM
 +pkglib_LTLIBRARIES += lvm.la
 +lvm_la_SOURCES = lvm.c
 +lvm_la_LDFLAGS = -module -avoid-version
 +lvm_la_LIBADD = $(BUILD_WITH_LIBLVM2APP_LIBS)
 +collectd_LDADD += "-dlopen" lvm.la
 +collectd_DEPENDENCIES += lvm.la
 +endif
 +
  if BUILD_PLUGIN_MADWIFI
  pkglib_LTLIBRARIES += madwifi.la
  madwifi_la_SOURCES = madwifi.c madwifi.h
@@@ -754,8 -727,8 +754,8 @@@ if BUILD_PLUGIN_NETLIN
  pkglib_LTLIBRARIES += netlink.la
  netlink_la_SOURCES = netlink.c
  netlink_la_LDFLAGS = -module -avoid-version
 -netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBNETLINK_CFLAGS)
 -netlink_la_LIBADD = $(BUILD_WITH_LIBNETLINK_LIBS)
 +netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
 +netlink_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
  collectd_LDADD += "-dlopen" netlink.la
  collectd_DEPENDENCIES += netlink.la
  endif
@@@ -1062,16 -1035,6 +1062,16 @@@ collectd_LDADD += "-dlopen" serial.l
  collectd_DEPENDENCIES += serial.la
  endif
  
 +if BUILD_PLUGIN_SIGROK
 +pkglib_LTLIBRARIES += sigrok.la
 +sigrok_la_SOURCES = sigrok.c
 +sigrok_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSIGROK_CFLAGS)
 +sigrok_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSIGROK_LDFLAGS)
 +sigrok_la_LIBADD = -lsigrok
 +collectd_LDADD += "-dlopen" sigrok.la
 +collectd_DEPENDENCIES += sigrok.la
 +endif
 +
  if BUILD_PLUGIN_SNMP
  pkglib_LTLIBRARIES += snmp.la
  snmp_la_SOURCES = snmp.c
@@@ -1089,16 -1052,6 +1089,16 @@@ collectd_LDADD += "-dlopen" snmp.l
  collectd_DEPENDENCIES += snmp.la
  endif
  
 +if BUILD_PLUGIN_STATSD
 +pkglib_LTLIBRARIES += statsd.la
 +statsd_la_SOURCES = statsd.c \
 +                    utils_latency.h utils_latency.c
 +statsd_la_LDFLAGS = -module -avoid-version
 +statsd_la_LIBADD = -lpthread
 +collectd_LDADD += "-dlopen" statsd.la
 +collectd_DEPENDENCIES += statsd.la
 +endif
 +
  if BUILD_PLUGIN_SWAP
  pkglib_LTLIBRARIES += swap.la
  swap_la_SOURCES = swap.c
@@@ -1318,16 -1271,6 +1318,16 @@@ collectd_LDADD += "-dlopen" uuid.l
  collectd_DEPENDENCIES += uuid.la
  endif
  
 +if BUILD_PLUGIN_MIC
 +pkglib_LTLIBRARIES += mic.la
 +mic_la_SOURCES = mic.c
 +mic_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_MIC_LIBPATH)
 +mic_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
 +mic_la_LIBADD = $(BUILD_WITH_MIC_LDADD)
 +collectd_LDADD += "-dlopen" mic.la
 +collectd_DEPENDENCIES += mic.la
 +endif
 +
  if BUILD_PLUGIN_VARNISH
  pkglib_LTLIBRARIES += varnish.la
  varnish_la_SOURCES = varnish.c
@@@ -1432,11 -1375,7 +1432,11 @@@ pkglib_LTLIBRARIES += zfs_arc.l
  zfs_arc_la_SOURCES = zfs_arc.c
  zfs_arc_la_CFLAGS = $(AM_CFLAGS)
  zfs_arc_la_LDFLAGS = -module -avoid-version
 +if BUILD_FREEBSD
 +zfs_arc_la_LIBADD = -lm
 +else
  zfs_arc_la_LIBADD = -lkstat
 +endif
  collectd_LDADD += "-dlopen" zfs_arc.la
  collectd_DEPENDENCIES += zfs_arc.la
  endif
diff --combined src/collectd.conf.pod
@@@ -6,9 -6,9 +6,9 @@@ collectd.conf - Configuration for the s
  
  =head1 SYNOPSIS
  
 -  BaseDir "/path/to/data/"
 -  PIDFile "/path/to/pidfile/collectd.pid"
 -  Server  "123.123.123.123" 12345
 +  BaseDir "/var/lib/collectd"
 +  PIDFile "/run/collectd.pid"
 +  Interval 10.0
    
    LoadPlugin cpu
    LoadPlugin load
@@@ -16,9 -16,6 +16,9 @@@
    <LoadPlugin df>
      Interval 3600
    </LoadPlugin>
 +  <Plugin df>
 +    ValuesPercentage true
 +  </Plugin>
    
    LoadPlugin ping
    <Plugin ping>
@@@ -31,9 -28,7 +31,9 @@@
  This config file controls how the system statistics collection daemon
  B<collectd> behaves. The most significant option is B<LoadPlugin>, which
  controls which plugins to load. These plugins ultimately define collectd's
 -behavior.
 +behavior. If the B<AutoLoadPlugin> option has been enabled, the explicit
 +B<LoadPlugin> lines may be omitted for all plugins with a configuration block,
 +i.e. a C<E<lt>PluginE<nbsp>...E<gt>> block.
  
  The syntax of this config file is similar to the config file of the famous
  I<Apache> webserver. Each line contains either an option (a key and a list of
@@@ -60,9 -55,8 +60,9 @@@ indenting the wrapped lines
  The configuration is read and processed in order, i.e. from top to bottom. So
  the plugins are loaded in the order listed in this config file. It is a good
  idea to load any logging plugins first in order to catch messages from plugins
 -during configuration. Also, the C<LoadPlugin> option B<must> occur B<before>
 -the appropriate C<E<lt>Plugin ...E<gt>> block.
 +during configuration. Also, unless B<AutoLoadPlugin> is enabled, the
 +B<LoadPlugin> option I<must> occur I<before> the appropriate
 +C<E<lt>B<Plugin> ...E<gt>> block.
  
  =head1 GLOBAL OPTIONS
  
@@@ -76,33 -70,17 +76,33 @@@ directory for the daemon
  
  =item B<LoadPlugin> I<Plugin>
  
 -Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
 -will be mostly useless.
 +Loads the plugin I<Plugin>. This is required to load plugins, unless the
 +B<AutoLoadPlugin> option is enabled (see below). Without any loaded plugins,
 +I<collectd> will be mostly useless.
  
 -Starting with collectd 4.9, this may also be a block in which further options
 -affecting the behavior of B<LoadPlugin> may be specified. The following
 -options are allowed inside a B<LoadPlugin> block:
 +Only the first B<LoadPlugin> statement or block for a given plugin name has any
 +effect. This is useful when you want to split up the configuration into smaller
 +files and want each file to be "self contained", i.e. it contains a B<Plugin>
 +block I<and> then appropriate B<LoadPlugin> statement. The downside is that if
 +you have multiple conflicting B<LoadPlugin> blocks, e.g. when they specify
 +different intervals, only one of them (the first one encountered) will take
 +effect and all others will be silently ignored.
  
 -  <LoadPlugin perl>
 -    Globals true
 -    Interval 10
 -  </LoadPlugin>
 +B<LoadPlugin> may either be a simple configuration I<statement> or a I<block>
 +with additional options, affecting the behavior of B<LoadPlugin>. A simple
 +statement looks like this:
 +
 + LoadPlugin "cpu"
 +
 +Options inside a B<LoadPlugin> block can override default settings and
 +influence the way plugins are loaded, e.g.:
 +
 + <LoadPlugin perl>
 +   Globals true
 +   Interval 60
 + </LoadPlugin>
 +
 +The following options are valid inside B<LoadPlugin> blocks:
  
  =over 4
  
@@@ -133,20 -111,7 +133,20 @@@ interval, that setting will take preced
  
  =back
  
 -=item B<Include> I<Path>
 +=item B<AutoLoadPlugin> B<false>|B<true>
 +
 +When set to B<false> (the default), each plugin needs to be loaded explicitly,
 +using the B<LoadPlugin> statement documented above. If a
 +B<E<lt>PluginE<nbsp>...E<gt>> block is encountered and no configuration
 +handling callback for this plugin has been registered, a warning is logged and
 +the block is ignored.
 +
 +When set to B<true>, explicit B<LoadPlugin> statements are not required. Each
 +B<E<lt>PluginE<nbsp>...E<gt>> block acts as if it was immediately preceded by a
 +B<LoadPlugin> statement. B<LoadPlugin> statements are still required for
 +plugins that don't provide any configuration, e.g. the I<Load plugin>.
 +
 +=item B<Include> I<Path> [I<pattern>]
  
  If I<Path> points to a file, includes that file. If I<Path> points to a
  directory, recursively includes all files within that directory and its
@@@ -241,36 -206,6 +241,36 @@@ Number of threads to start for dispatch
  default value is B<5>, but you may want to increase this if you have more than
  five plugins that may take relatively long to write to.
  
 +=item B<WriteQueueLimitHigh> I<HighNum>
 +
 +=item B<WriteQueueLimitLow> I<LowNum>
 +
 +Metrics are read by the I<read threads> and then put into a queue to be handled
 +by the I<write threads>. If one of the I<write plugins> is slow (e.g. network
 +timeouts, I/O saturation of the disk) this queue will grow. In order to avoid
 +running into memory issues in such a case, you can limit the size of this
 +queue.
 +
 +By default, there is no limit and memory may grow indefinitely. This is most
 +likely not an issue for clients, i.e. instances that only handle the local
 +metrics. For servers it is recommended to set this to a non-zero value, though.
 +
 +You can set the limits using B<WriteQueueLimitHigh> and B<WriteQueueLimitLow>.
 +Each of them takes a numerical argument which is the number of metrics in the
 +queue. If there are I<HighNum> metrics in the queue, any new metrics I<will> be
 +dropped. If there are less than I<LowNum> metrics in the queue, all new metrics
 +I<will> be enqueued. If the number of metrics currently in the queue is between
 +I<LowNum> and I<HighNum>, the metric is dropped with a probability that is
 +proportional to the number of metrics in the queue (i.e. it increases linearly
 +until it reaches 100%.)
 +
 +If B<WriteQueueLimitHigh> is set to non-zero and B<WriteQueueLimitLow> is
 +unset, the latter will default to half of B<WriteQueueLimitHigh>.
 +
 +If you do not want to randomly drop values when the queue size is between
 +I<LowNum> and I<HighNum>, set If B<WriteQueueLimitHigh> and
 +B<WriteQueueLimitLow> to same value.
 +
  =item B<Hostname> I<Name>
  
  Sets the hostname that identifies a host. If you omit this setting, the
@@@ -751,29 -686,6 +751,29 @@@ default for backwards compatibility, th
  
  =back
  
 +=head2 Plugin C<aquaero>
 +
 +This plugin collects the value of the available sensors in an
 +I<AquaeroE<nbsp>5> board. AquaeroE<nbsp>5 is a water-cooling controller board,
 +manufactured by Aqua Computer GmbH L<http://www.aquacomputer.de/>, with a USB2
 +connection for monitoring and configuration. The board can handle multiple
 +temperature sensors, fans, water pumps and water level sensors and adjust the
 +output settings such as fan voltage or power used by the water pump based on
 +the available inputs using a configurable controller included in the board.
 +This plugin collects all the available inputs as well as some of the output
 +values chosen by this controller. The plugin is based on the I<libaquaero5>
 +library provided by I<aquatools-ng>.
 +
 +=over 4
 +
 +=item B<Device> I<DevicePath>
 +
 +Device path of the AquaeroE<nbsp>5's USB HID (human interface device), usually
 +in the form C</dev/usb/hiddevX>. If this option is no set the plugin will try
 +to auto-detect the Aquaero 5 USB device based on vendor-ID and product-ID.
 +
 +=back
 +
  =head2 Plugin C<ascent>
  
  This plugin collects information about an Ascent server, a free server for the
@@@ -971,29 -883,6 +971,29 @@@ By default no detailed zone informatio
  
  =back
  
 +=head2 Plugin C<cgroups>
 +
 +This plugin collects the CPU user/system time for each I<cgroup> by reading the
 +F<cpuacct.stat> files in the first cpuacct-mountpoint (typically
 +F</sys/fs/cgroup/cpu.cpuacct> on machines using systemd).
 +
 +=over 4
 +
 +=item B<CGroup> I<Directory>
 +
 +Select I<cgroup> based on the name. Whether only matching I<cgroups> are
 +collected or if they are ignored is controlled by the B<IgnoreSelected> option;
 +see below.
 +
 +=item B<IgnoreSelected> B<true>|B<false>
 +
 +Invert the selection: If set to true, all cgroups I<except> the ones that
 +match any one of the criteria are collected. By default only selected
 +cgroups are collected if a selection is made. If no selection is configured
 +at all, B<all> cgroups are selected.
 +
 +=back
 +
  =head2 Plugin C<cpufreq>
  
  This plugin doesn't have any options. It reads
@@@ -1116,15 -1005,13 +1116,15 @@@ is set to B<true>, B<Match> blocks are 
  
  =head2 Plugin C<curl_json>
  
 -The B<curl_json plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and
 -B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) to retrieve JSON data
 -via cURL. This can be used to collect values from CouchDB documents (which are
 -stored JSON notation), for example.
 +The B<curl_json plugin> collects values from JSON data to be parsed by
 +B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) retrieved via
 +either B<libcurl> (L<http://curl.haxx.se/>) or read directly from a
 +unix socket. The former can be used, for example, to collect values
 +from CouchDB documents (which are stored JSON notation), and the
 +latter to collect values from a uWSGI stats socket.
  
 -The following example will collect several values from the built-in `_stats'
 -runtime statistics module of CouchDB
 +The following example will collect several values from the built-in
 +C<_stats> runtime statistics module of I<CouchDB>
  (L<http://wiki.apache.org/couchdb/Runtime_Statistics>).
  
    <Plugin curl_json>
      </URL>
    </Plugin>
  
 -In the B<Plugin> block, there may be one or more B<URL> blocks, each defining
 -a URL to be fetched via HTTP (using libcurl) and one or more B<Key> blocks.
 -The B<Key> string argument must be in a path format, which is used to collect a
 -value from a JSON map object. If a path element of B<Key> is the
 -I<*>E<nbsp>wildcard, the values for all keys will be collectd.
 +This example will collect data directly from a I<uWSGI> "Stats Server" socket.
 +
 +  <Plugin curl_json>
 +    <Sock "/var/run/uwsgi.stats.sock">
 +      Instance "uwsgi"
 +      <Key "workers/*/requests">
 +        Type "http_requests"
 +      </Key>
 +
 +      <Key "workers/*/apps/*/requests">
 +        Type "http_requests"
 +      </Key>
 +    </Sock>
 +  </Plugin>
 +
 +In the B<Plugin> block, there may be one or more B<URL> blocks, each
 +defining a URL to be fetched via HTTP (using libcurl) or B<Sock>
 +blocks defining a unix socket to read JSON from directly.  Each of
 +these blocks may have one or more B<Key> blocks.
 +
 +The B<Key> string argument must be in a path format. Each component is
 +used to match the key from a JSON map or the index of an JSON
 +array. If a path component of a B<Key> is a I<*>E<nbsp>wildcard, the
 +values for all map keys or array indices will be collectd.
  
  The following options are valid within B<URL> blocks:
  
@@@ -1622,20 -1490,6 +1622,20 @@@ Enable this option if inodes are a scar
  many small files are stored on the disk. This is a usual scenario for mail
  transfer agents and web caches.
  
 +=item B<ValuesAbsolute> B<true>|B<false>
 +
 +Enables or disables reporting of free, used and used disk space in 1K-blocks. 
 +Defaults to true.
 +
 +=item B<ValuesPercentage> B<true>|B<false>
 +
 +Enables or disables reporting of free, used and used disk space in percentage.
 +Defaults to false.
 +
 +This is useful for deploying collectd on the cloud, where machines with
 +different disk size may exist. Then it is more practical to configure thresholds
 +based on relative disk size.
 +
  =back
  
  =head2 Plugin C<disk>
@@@ -2384,7 -2238,7 +2384,7 @@@ interpreted. For a description of matc
  
  =head2 Plugin C<memcached>
  
 -The C<memcached plugin> connects to a memcached server and queries statistics
 +The B<memcached plugin> connects to a memcached server and queries statistics
  about cache utilization, memory and bandwidth used.
  L<http://www.danga.com/memcached/>
  
@@@ -2416,166 -2270,6 +2416,166 @@@ setting is given, the B<Host> and B<Por
  
  =back
  
 +=head2 Plugin C<mic>
 +
 +The B<mic plugin> gathers CPU statistics, memory usage and temperatures from
 +Intel's Many Integrated Core (MIC) systems.
 +
 +B<Synopsis:>
 +
 + <Plugin mic>
 +   ShowCPU true
 +   ShowCPUCores true
 +   ShowMemory true
 +   
 +   ShowTemperatures true
 +   Temperature vddg
 +   Temperature vddq
 +   IgnoreSelectedTemperature true
 +
 +   ShowPower true
 +   Power total0
 +   Power total1
 +   IgnoreSelectedPower true   
 + </Plugin>
 +
 +The following options are valid inside the B<PluginE<nbsp>mic> block:
 +
 +=over 4
 +
 +=item B<ShowCPU> B<true>|B<false>
 +
 +If enabled (the default) a sum of the CPU usage accross all cores is reported.
 +
 +=item B<ShowCPUCores> B<true>|B<false>
 +
 +If enabled (the default) per-core CPU usage is reported.
 +
 +=item B<ShowMemory> B<true>|B<false>
 +
 +If enabled (the default) the physical memory usage of the MIC system is
 +reported.
 +
 +=item B<ShowTemperatures> B<true>|B<false>
 +
 +If enabled (the default) various temperatures of the MIC system are reported.
 +
 +=item B<Temperature> I<Name>
 +
 +This option controls which temperatures are being reported. Whether matching
 +temperatures are being ignored or I<only> matching temperatures are reported
 +depends on the B<IgnoreSelectedTemperature> setting below. By default I<all>
 +temperatures are reported.
 +
 +=item B<IgnoreSelectedTemperature> B<false>|B<true>
 +
 +Controls the behavior of the B<Temperature> setting above. If set to B<false>
 +(the default) only temperatures matching a B<Temperature> option are reported
 +or, if no B<Temperature> option is specified, all temperatures are reported. If
 +set to B<true>, matching temperatures are I<ignored> and all other temperatures
 +are reported.
 +
 +Known temperature names are:
 +
 +=over 4
 +
 +=item die
 +
 +Die of the CPU
 +
 +=item devmem
 +
 +Device Memory
 +
 +=item fin
 +
 +Fan In
 +
 +=item fout
 +
 +Fan Out 
 +
 +=item vccp
 +
 +Voltage ccp
 +
 +=item vddg
 +
 +Voltage ddg
 +
 +=item vddq
 +
 +Voltage ddq
 +
 +=back
 +
 +=item B<ShowPower> B<true>|B<false>
 +
 +If enabled (the default) various temperatures of the MIC system are reported.
 +
 +=item B<Power> I<Name>
 +
 +This option controls which power readings are being reported. Whether matching
 +power readings are being ignored or I<only> matching power readings are reported
 +depends on the B<IgnoreSelectedPower> setting below. By default I<all>
 +power readings are reported.
 +
 +=item B<IgnoreSelectedPower> B<false>|B<true>
 +
 +Controls the behavior of the B<Power> setting above. If set to B<false>
 +(the default) only power readings matching a B<Power> option are reported
 +or, if no B<Power> option is specified, all power readings are reported. If
 +set to B<true>, matching power readings are I<ignored> and all other power readings
 +are reported.
 +
 +Known power names are:
 +
 +=over 4
 +
 +=item total0
 +
 +Total power utilization averaged over Time Window 0 (uWatts). 
 +
 +=item total1
 +
 +Total power utilization averaged over Time Window 0 (uWatts). 
 +
 +=item inst
 +
 +Instantaneous power (uWatts).
 +
 +=item imax
 +
 +Max instantaneous power (uWatts). 
 +
 +=item pcie
 +
 +PCI-E connector power (uWatts). 
 +
 +=item c2x3
 +
 +2x3 connector power (uWatts). 
 +
 +=item c2x4
 +
 +2x4 connector power (uWatts). 
 +
 +=item vccp
 +
 +Core rail (uVolts). 
 +
 +=item vddg
 +
 +Uncore rail (uVolts). 
 +
 +=item vddq
 +
 +Memory subsystem rail (uVolts). 
 +
 +=back
 +
 +=back
 +
  =head2 Plugin C<modbus>
  
  The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
@@@ -2583,7 -2277,7 +2583,7 @@@ register values. It supports reading si
  values), large integer values (unsigned 32E<nbsp>bit values) and floating point
  values (two registers interpreted as IEEE floats in big endian notation).
  
 -Synopsis:
 +B<Synopsis:>
  
   <Data "voltage-input-1">
     RegisterBase 0
@@@ -5213,142 -4907,12 +5213,142 @@@ and all other sensors are collected
  
  =back
  
 +=head2 Plugin C<sigrok>
 +
 +The I<sigrok plugin> uses I<libsigrok> to retrieve measurements from any device
 +supported by the L<sigrok|http://sigrok.org/> project.
 +
 +B<Synopsis>
 +
 + <Plugin sigrok>
 +   LogLevel 3
 +   <Device "AC Voltage">
 +      Driver "fluke-dmm"
 +      MinimumInterval 10
 +      Conn "/dev/ttyUSB2"
 +   </Device>
 +   <Device "Sound Level">
 +      Driver "cem-dt-885x"
 +      Conn "/dev/ttyUSB1"
 +   </Device>
 + </Plugin>
 +
 +=over 4
 +
 +=item B<LogLevel> B<0-5>
 +
 +The I<sigrok> logging level to pass on to the I<collectd> log, as a number
 +between B<0> and B<5> (inclusive). These levels correspond to C<None>,
 +C<Errors>, C<Warnings>, C<Informational>, C<Debug >and C<Spew>, respectively.
 +The default is B<2> (C<Warnings>). The I<sigrok> log messages, regardless of
 +their level, are always submitted to I<collectd> at its INFO log level.
 +
 +=item E<lt>B<Device> I<Name>E<gt>
 +
 +A sigrok-supported device, uniquely identified by this section's options. The
 +I<Name> is passed to I<collectd> as the I<plugin instance>.
 +
 +=item B<Driver> I<DriverName>
 +
 +The sigrok driver to use for this device.
 +
 +=item B<Conn> I<ConnectionSpec>
 +
 +If the device cannot be auto-discovered, or more than one might be discovered
 +by the driver, I<ConnectionSpec> specifies the connection string to the device.
 +It can be of the form of a device path (e.g.E<nbsp>C</dev/ttyUSB2>), or, in
 +case of a non-serial USB-connected device, the USB I<VendorID>B<.>I<ProductID>
 +separated by a period (e.g.E<nbsp>C<0403.6001>). A USB device can also be
 +specified as I<Bus>B<.>I<Address> (e.g.E<nbsp>C<1.41>).
 +
 +=item B<SerialComm> I<SerialSpec>
 +
 +For serial devices with non-standard port settings, this option can be used
 +to specify them in a form understood by I<sigrok>, e.g.E<nbsp>C<9600/8n1>.
 +This should not be necessary; drivers know how to communicate with devices they
 +support.
 +
 +=item B<MinimumInterval> I<Seconds>
 +
 +Specifies the minimum time between measurement dispatches to I<collectd>, in
 +seconds. Since some I<sigrok> supported devices can acquire measurements many
 +times per second, it may be necessary to throttle these. For example, the
 +I<RRD plugin> cannot process writes more than once per second.
 +
 +The default B<MinimumInterval> is B<0>, meaning measurements received from the
 +device are always dispatched to I<collectd>. When throttled, unused
 +measurements are discarded.
 +
 +=back
 +
  =head2 Plugin C<snmp>
  
  Since the configuration of the C<snmp plugin> is a little more complicated than
  other plugins, its documentation has been moved to an own manpage,
  L<collectd-snmp(5)>. Please see there for details.
  
 +=head2 Plugin C<statsd>
 +
 +The I<statsd plugin> listens to a UDP socket, reads "events" in the statsd
 +protocol and dispatches rates or other aggregates of these numbers
 +periodically.
 +
 +The plugin implements the I<Counter>, I<Timer>, I<Gauge> and I<Set> types which
 +are dispatched as the I<collectd> types C<derive>, C<latency>, C<gauge> and
 +C<objects> respectively.
 +
 +The following configuration options are valid:
 +
 +=over 4
 +
 +=item B<Host> I<Host>
 +
 +Bind to the hostname / address I<Host>. By default, the plugin will bind to the
 +"any" address, i.e. accept packets sent to any of the hosts addresses.
 +
 +=item B<Port> I<Port>
 +
 +UDP port to listen to. This can be either a service name or a port number.
 +Defaults to C<8125>.
 +
 +=item B<DeleteCounters> B<false>|B<true>
 +
 +=item B<DeleteTimers> B<false>|B<true>
 +
 +=item B<DeleteGauges> B<false>|B<true>
 +
 +=item B<DeleteSets> B<false>|B<true>
 +
 +These options control what happens if metrics are not updated in an interval.
 +If set to B<False>, the default, metrics are dispatched unchanged, i.e. the
 +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<TimerPercentile> I<Percent>
 +
 +Calculate and dispatch the configured percentile, i.e. compute the latency, so
 +that I<Percent> of all reported timers are smaller than or equal to the
 +computed latency. This is useful for cutting off the long tail latency, as it's
 +often done in I<Service Level Agreements> (SLAs).
 +
 +Different percentiles can be calculated by setting this option several times.
 +If none are specified, no percentiles are calculated / dispatched.
 +
 +=item B<TimerLower> B<false>|B<true>
 +
 +=item B<TimerUpper> B<false>|B<true>
 +
 +=item B<TimerSum> B<false>|B<true>
 +
 +=item B<TimerCount> B<false>|B<true>
 +
 +Calculate and dispatch various values out of I<Timer> metrics received during
 +an interval. If set to B<False>, the default, these values aren't calculated /
 +dispatched.
 +
 +=back
 +
  =head2 Plugin C<swap>
  
  The I<Swap plugin> collects information about used and available swap space. On
@@@ -5990,17 -5554,6 +5990,17 @@@ and closed connections. True by default
  Statistics about the shared memory log, a memory region to store
  log messages which is flushed to disk when full. True by default.
  
 +=item B<CollectBan> B<true>|B<false>
 +
 +Statistics about ban operations, such as number of bans added, retired, and
 +number of objects tested against ban operations. Only available with Varnish
 +3.x. False by default.
 +
 +=item B<CollectDirectorDNS> B<true>|B<false>
 +
 +DNS director lookup cache statistics. Only available with Varnish 3.x. False by
 +default.
 +
  =item B<CollectESI> B<true>|B<false>
  
  Edge Side Includes (ESI) parse statistics. False by default.
@@@ -6014,27 -5567,10 +6014,27 @@@ Statistics about fetches (HTTP request
  Inserts and look-ups in the crit bit tree based hash. Look-ups are
  divided into locked and unlocked look-ups. False by default.
  
 +=item B<CollectObjects> B<true>|B<false>
 +
 +Statistics on cached objects: number of objects expired, nuked (prematurely
 +expired), saved, moved, etc. False by default.
 +
 +=item B<CollectPurge> B<true>|B<false>
 +
 +Statistics about purge operations, such as number of purges added, retired, and
 +number of objects tested against purge operations. Only available with Varnish
 +2.x. False by default.
 +
 +=item B<CollectSession> B<true>|B<false>
 +
 +Client session statistics. Number of past and current sessions, session herd and
 +linger counters, etc. False by default.
 +
  =item B<CollectSMA> B<true>|B<false>
  
 -malloc or umem (umem_alloc(3MALLOC) based) storage statistics.
 -The umem storage component is Solaris specific. False by default.
 +malloc or umem (umem_alloc(3MALLOC) based) storage statistics. The umem storage
 +component is Solaris specific. Only available with Varnish 2.x. False by
 +default.
  
  =item B<CollectSMS> B<true>|B<false>
  
@@@ -6043,28 -5579,13 +6043,28 @@@ component is used internally only. Fals
  
  =item B<CollectSM> B<true>|B<false>
  
 -file (memory mapped file) storage statistics. False by default.
 +file (memory mapped file) storage statistics. Only available with Varnish 2.x.
 +False by default.
 +
 +=item B<CollectStruct> B<true>|B<false>
 +
 +Current varnish internal state statistics. Number of current sessions, objects
 +in cache store, open connections to backends (with Varnish 2.x), etc. False by
 +default.
  
  =item B<CollectTotals> B<true>|B<false>
  
  Collects overview counters, such as the number of sessions created,
  the number of requests and bytes transferred. False by default.
  
 +=item B<CollectUptime> B<true>|B<false>
 +
 +Varnish uptime. False by default.
 +
 +=item B<CollectVCL> B<true>|B<false>
 +
 +Number of total (available + discarded) VCL (config files). False by default.
 +
  =item B<CollectWorkers> B<true>|B<false>
  
  Collect statistics about worker threads. False by default.
@@@ -6109,9 -5630,9 +6109,9 @@@ iptables to feed data for the guest IP
  
  The C<write_graphite> plugin writes data to I<Graphite>, an open-source metrics
  storage and graphing project. The plugin connects to I<Carbon>, the data layer
 -of I<Graphite>, and sends data via the "line based" protocol (per default using
 -portE<nbsp>2003). The data will be sent in blocks of at most 1428 bytes to
 -minimize the number of network packets.
 +of I<Graphite>, via I<TCP> or I<UDP> and sends data via the "line based"
 +protocol (per default using portE<nbsp>2003). The data will be sent in blocks
 +of at most 1428 bytes to minimize the number of network packets.
  
  Synopsis:
  
     <Node "example">
       Host "localhost"
       Port "2003"
 +     Protocol "tcp"
 +     LogSendErrors true
       Prefix "collectd"
     </Node>
   </Plugin>
@@@ -6138,17 -5657,6 +6138,17 @@@ Hostname or address to connect to. Defa
  
  Service name or port number to connect to. Defaults to C<2003>.
  
 +=item B<Protocol> I<String>
 +
 +Protocol to use when connecting to I<Graphite>. Defaults to C<tcp>.
 +
 +=item B<LogSendErrors> B<false>|B<true>
 +
 +If set to B<true> (the default), logs errors when sending data to I<Graphite>.
 +If set to B<false>, it will not log the errors. This is especially useful when
 +using Protocol UDP since many times we want to use the "fire-and-forget"
 +approach and logging errors fills syslog with unneeded messages.
 +
  =item B<Prefix> I<String>
  
  When set, I<String> is added in front of the host name. Dots and whitespace are
@@@ -6304,6 -5812,61 +6304,61 @@@ number
  
  =back
  
+ =head2 Plugin C<write_redis>
+ The I<write_redis plugin> submits values to I<Redis>, a data structure server.
+ Synopsis:
+   <Plugin "write_redis">
+     <Node "example">
+         Host "localhost"
+         Port "6379"
+         Timeout 1000
+     </Node>
+   </Plugin>
+ Values are submitted to I<Sorted Sets>, using the metric name as the key, and
+ the timestamp as the score. Retrieving a date range can then be done using the
+ C<ZRANGEBYSCORE> I<Redis> command. Additionnally, all the identifiers of these
+ I<Sorted Sets> are kept in a I<Set> called C<collectd/values> and can be
+ retrieved using the C<SMEMBERS> I<Redis> command. See
+ L<http://redis.io/commands#sorted_set> and L<http://redis.io/commands#set> for
+ details.
+ The information shown in the synopsis above is the I<default configuration>
+ which is used by the plugin if no configuration is present.
+ The plugin can send values to multiple instances of I<Redis> by specifying
+ one B<Node> block for each instance. Within the B<Node> blocks, the following
+ options are available:
+ =over 4
+ =item B<Node> I<Nodename>
+ The B<Node> block identifies a new I<Redis> node, that is a new I<Redis>
+ instance running in an specified host and port. The name for node is a
+ canonical identifier which is used as I<plugin instance>. It is limited to
+ 64E<nbsp>characters in length.
+ =item B<Host> I<Hostname>
+ The B<Host> option is the hostname or IP-address where the I<Redis> instance is
+ running on.
+ =item B<Port> I<Port>
+ The B<Port> option is the TCP port on which the Redis instance accepts
+ connections. Either a service name of a port number may be given. Please note
+ that numerical port numbers must be given as a string, too.
+ =item B<Timeout> I<Timeout in miliseconds>
+ The B<Timeout> option sets the socket connection timeout, in milliseconds.
+ =back
  =head2 Plugin C<write_riemann>
  
  The I<write_riemann plugin> will send values to I<Riemann>, a powerfull stream
@@@ -6319,7 -5882,7 +6374,7 @@@ Synopsis
       Protocol UDP
       StoreRates true
       AlwaysAppendDS false
 -     Delay 10
 +     TTLFactor 2.0
     </Node>
     Tag "foobar"
   </Plugin>
@@@ -6366,15 -5929,6 +6421,15 @@@ If set the B<true>, append the name of 
  identifies a metric in I<Riemann>. If set to B<false> (the default), this is
  only done when there is more than one DS.
  
 +=item B<TTLFactor> I<Factor>
 +
 +I<Riemann> events have a I<Time to Live> (TTL) which specifies how long each
 +event is considered active. I<collectd> populates this field based on the
 +metrics interval setting. This setting controls the factor with which the
 +interval is multiplied to set the TTL. The default value is B<2.0>. Unless you
 +know exactly what you're doing, you should only increase this setting from its
 +default value.
 +
  =back
  
  =item B<Tag> I<String>
diff --combined src/plugin.c
@@@ -31,7 -31,6 +31,7 @@@
  #include "utils_llist.h"
  #include "utils_heap.h"
  #include "utils_time.h"
 +#include "utils_random.h"
  
  #if HAVE_PTHREAD_H
  # include <pthread.h>
@@@ -62,7 -61,7 +62,7 @@@ struct read_func_
  #define rf_ctx rf_super.cf_ctx
        callback_func_t rf_super;
        char rf_group[DATA_MAX_NAME_LEN];
 -      char rf_name[DATA_MAX_NAME_LEN];
 +      char *rf_name;
        int rf_type;
        cdtime_t rf_interval;
        cdtime_t rf_effective_interval;
@@@ -82,8 -81,6 +82,8 @@@ struct write_queue_
  /*
   * Private variables
   */
 +static c_avl_tree_t *plugins_loaded = NULL;
 +
  static llist_t *list_init;
  static llist_t *list_write;
  static llist_t *list_flush;
@@@ -109,7 -106,6 +109,7 @@@ static int             read_threads_nu
  
  static write_queue_t  *write_queue_head;
  static write_queue_t  *write_queue_tail;
 +static long            write_queue_length = 0;
  static _Bool           write_loop = 1;
  static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
  static pthread_cond_t  write_cond = PTHREAD_COND_INITIALIZER;
@@@ -119,9 -115,6 +119,9 @@@ static size_t          write_threads_nu
  static pthread_key_t   plugin_ctx_key;
  static _Bool           plugin_ctx_key_initialized = 0;
  
 +static long            write_limit_high = 0;
 +static long            write_limit_low = 0;
 +
  /*
   * Static functions
   */
@@@ -339,7 -332,7 +339,7 @@@ static int plugin_load_file (char *file
  
                ssnprintf (errbuf, sizeof (errbuf),
                                "lt_dlopen (\"%s\") failed: %s. "
-                               "The most common cause for this problem are "
+                               "The most common cause for this problem is "
                                "missing dependencies. Use ldd(1) to check "
                                "the dependencies of the plugin "
                                "/ shared object.",
@@@ -444,7 -437,6 +444,7 @@@ static void *plugin_read_thread (void _
                {
                        DEBUG ("plugin_read_thread: Destroying the `%s' "
                                        "callback.", rf->rf_name);
 +                      sfree (rf->rf_name);
                        destroy_callback ((callback_func_t *) rf);
                        rf = NULL;
                        continue;
@@@ -675,13 -667,11 +675,13 @@@ static int plugin_write_enqueue (value_
        {
                write_queue_head = q;
                write_queue_tail = q;
 +              write_queue_length = 1;
        }
        else
        {
                write_queue_tail->next = q;
                write_queue_tail = q;
 +              write_queue_length += 1;
        }
  
        pthread_cond_signal (&write_cond);
@@@ -708,11 -698,8 +708,11 @@@ static value_list_t *plugin_write_deque
  
        q = write_queue_head;
        write_queue_head = q->next;
 -      if (write_queue_head == NULL)
 +      write_queue_length -= 1;
 +      if (write_queue_head == NULL) {
                write_queue_tail = NULL;
 +              assert(0 == write_queue_length);
 +              }
  
        pthread_mutex_unlock (&write_lock);
  
@@@ -815,7 -802,6 +815,7 @@@ static void stop_write_threads (void) /
        }
        write_queue_head = NULL;
        write_queue_tail = NULL;
 +      write_queue_length = 0;
        pthread_mutex_unlock (&write_lock);
  
        if (i > 0)
@@@ -844,52 -830,8 +844,52 @@@ void plugin_set_dir (const char *dir
        }
  }
  
 +static _Bool plugin_is_loaded (char const *name)
 +{
 +      int status;
 +
 +      if (plugins_loaded == NULL)
 +              plugins_loaded = c_avl_create ((void *) strcasecmp);
 +      assert (plugins_loaded != NULL);
 +
 +      status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
 +      return (status == 0);
 +}
 +
 +static int plugin_mark_loaded (char const *name)
 +{
 +      char *name_copy;
 +      int status;
 +
 +      name_copy = strdup (name);
 +      if (name_copy == NULL)
 +              return (ENOMEM);
 +
 +      status = c_avl_insert (plugins_loaded,
 +                      /* key = */ name_copy, /* value = */ NULL);
 +      return (status);
 +}
 +
 +static void plugin_free_loaded ()
 +{
 +      void *key;
 +      void *value;
 +
 +      if (plugins_loaded == NULL)
 +              return;
 +
 +      while (c_avl_pick (plugins_loaded, &key, &value) == 0)
 +      {
 +              sfree (key);
 +              assert (value == NULL);
 +      }
 +
 +      c_avl_destroy (plugins_loaded);
 +      plugins_loaded = NULL;
 +}
 +
  #define BUFSIZE 512
 -int plugin_load (const char *type, uint32_t flags)
 +int plugin_load (char const *plugin_name, uint32_t flags)
  {
        DIR  *dh;
        const char *dir;
        struct dirent *de;
        int status;
  
 +      if (plugin_name == NULL)
 +              return (EINVAL);
 +
 +      /* Check if plugin is already loaded and don't do anything in this
 +       * case. */
 +      if (plugin_is_loaded (plugin_name))
 +              return (0);
 +
        dir = plugin_get_dir ();
        ret = 1;
  
 +      /*
 +       * XXX: Magic at work:
 +       *
 +       * Some of the language bindings, for example the Python and Perl
 +       * plugins, need to be able to export symbols to the scripts they run.
 +       * For this to happen, the "Globals" flag needs to be set.
 +       * Unfortunately, this technical detail is hard to explain to the
 +       * average user and she shouldn't have to worry about this, ideally.
 +       * So in order to save everyone's sanity use a different default for a
 +       * handful of special plugins. --octo
 +       */
 +      if ((strcasecmp ("perl", plugin_name) == 0)
 +                      || (strcasecmp ("python", plugin_name) == 0))
 +              flags |= PLUGIN_FLAGS_GLOBAL;
 +
        /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
         * type when matching the filename */
 -      status = ssnprintf (typename, sizeof (typename), "%s.so", type);
 +      status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name);
        if ((status < 0) || ((size_t) status >= sizeof (typename)))
        {
 -              WARNING ("plugin_load: Filename too long: \"%s.so\"", type);
 +              WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name);
                return (-1);
        }
        typename_len = strlen (typename);
                if (status == 0)
                {
                        /* success */
 +                      plugin_mark_loaded (plugin_name);
                        ret = 0;
                        break;
                }
                else
                {
                        ERROR ("plugin_load: Load plugin \"%s\" failed with "
 -                                      "status %i.", type, status);
 +                                      "status %i.", plugin_name, status);
                }
        }
  
  
        if (filename[0] == 0)
                ERROR ("plugin_load: Could not find plugin \"%s\" in %s",
 -                              type, dir);
 +                              plugin_name, dir);
  
        return (ret);
  }
@@@ -1130,7 -1048,7 +1130,7 @@@ int plugin_register_read (const char *n
        rf->rf_udata.free_func = NULL;
        rf->rf_ctx = plugin_get_ctx ();
        rf->rf_group[0] = '\0';
 -      sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
 +      rf->rf_name = strdup (name);
        rf->rf_type = RF_SIMPLE;
        rf->rf_interval = plugin_get_interval ();
  
@@@ -1162,7 -1080,7 +1162,7 @@@ int plugin_register_complex_read (cons
                sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
        else
                rf->rf_group[0] = '\0';
 -      sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
 +      rf->rf_name = strdup (name);
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
                rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
@@@ -1455,8 -1373,7 +1455,8 @@@ int plugin_unregister_notification (con
  
  void plugin_init_all (void)
  {
 -      const char *chain_name;
 +      char const *chain_name;
 +      long write_threads_num;
        llentry_t *le;
        int status;
  
        chain_name = global_option_get ("PostCacheChain");
        post_cache_chain = fc_chain_get_by_name (chain_name);
  
 +      write_limit_high = global_option_get_long ("WriteQueueLimitHigh",
 +                      /* default = */ 0);
 +      if (write_limit_high < 0)
        {
 -              char const *tmp = global_option_get ("WriteThreads");
 -              int num = atoi (tmp);
 +              ERROR ("WriteQueueLimitHigh must be positive or zero.");
 +              write_limit_high = 0;
 +      }
  
 -              if (num < 1)
 -                      num = 5;
 +      write_limit_low = global_option_get_long ("WriteQueueLimitLow",
 +                      /* default = */ write_limit_high / 2);
 +      if (write_limit_low < 0)
 +      {
 +              ERROR ("WriteQueueLimitLow must be positive or zero.");
 +              write_limit_low = write_limit_high / 2;
 +      }
 +      else if (write_limit_low > write_limit_high)
 +      {
 +              ERROR ("WriteQueueLimitLow must not be larger than "
 +                              "WriteQueueLimitHigh.");
 +              write_limit_low = write_limit_high;
 +      }
  
 -              start_write_threads ((size_t) num);
 +      write_threads_num = global_option_get_long ("WriteThreads",
 +                      /* default = */ 5);
 +      if (write_threads_num < 1)
 +      {
 +              ERROR ("WriteThreads must be positive.");
 +              write_threads_num = 5;
        }
  
 +      start_write_threads ((size_t) write_threads_num);
 +
        if ((list_init == NULL) && (read_heap == NULL))
                return;
  
@@@ -1784,7 -1679,6 +1784,7 @@@ void plugin_shutdown_all (void
        destroy_all_callbacks (&list_shutdown);
        destroy_all_callbacks (&list_log);
  
 +      plugin_free_loaded ();
        plugin_free_data_sets ();
  } /* void plugin_shutdown_all */
  
@@@ -2006,76 -1900,10 +2006,76 @@@ static int plugin_dispatch_values_inter
        return (0);
  } /* int plugin_dispatch_values_internal */
  
 +static double get_drop_probability (void) /* {{{ */
 +{
 +      long pos;
 +      long size;
 +      long wql;
 +
 +      pthread_mutex_lock (&write_lock);
 +      wql = write_queue_length;
 +      pthread_mutex_unlock (&write_lock);
 +
 +      if (wql < write_limit_low)
 +              return (0.0);
 +      if (wql >= write_limit_high)
 +              return (1.0);
 +
 +      pos = 1 + wql - write_limit_low;
 +      size = 1 + write_limit_high - write_limit_low;
 +
 +      return (((double) pos) / ((double) size));
 +} /* }}} double get_drop_probability */
 +
 +static _Bool check_drop_value (void) /* {{{ */
 +{
 +      static cdtime_t last_message_time = 0;
 +      static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
 +
 +      double p;
 +      double q;
 +      int status;
 +
 +      if (write_limit_high == 0)
 +              return (0);
 +
 +      p = get_drop_probability ();
 +      if (p == 0.0)
 +              return (0);
 +
 +      status = pthread_mutex_trylock (&last_message_lock);
 +      if (status == 0)
 +      {
 +              cdtime_t now;
 +
 +              now = cdtime ();
 +              if ((now - last_message_time) > TIME_T_TO_CDTIME_T (1))
 +              {
 +                      last_message_time = now;
 +                      ERROR ("plugin_dispatch_values: Low water mark "
 +                                      "reached. Dropping %.0f%% of metrics.",
 +                                      100.0 * p);
 +              }
 +              pthread_mutex_unlock (&last_message_lock);
 +      }
 +
 +      if (p == 1.0)
 +              return (1);
 +
 +      q = cdrand_d ();
 +      if (q > p)
 +              return (1);
 +      else
 +              return (0);
 +} /* }}} _Bool check_drop_value */
 +
  int plugin_dispatch_values (value_list_t const *vl)
  {
        int status;
  
 +      if (check_drop_value ())
 +              return (0);
 +
        status = plugin_write_enqueue (vl);
        if (status != 0)
        {
diff --combined src/write_graphite.c
@@@ -35,8 -35,6 +35,8 @@@
    *   <Carbon>
    *     Host "localhost"
    *     Port "2003"
 +  *     Protocol "udp"
 +  *     LogSendErrors true
    *     Prefix "collectd"
    *   </Carbon>
    * </Plugin>
  # define WG_DEFAULT_SERVICE "2003"
  #endif
  
 +#ifndef WG_DEFAULT_PROTOCOL
 +# define WG_DEFAULT_PROTOCOL "tcp"
 +#endif
 +
 +#ifndef WG_DEFAULT_LOG_SEND_ERRORS
 +# define WG_DEFAULT_LOG_SEND_ERRORS 1
 +#endif
 +
  #ifndef WG_DEFAULT_ESCAPE
  # define WG_DEFAULT_ESCAPE '_'
  #endif
@@@ -94,8 -84,6 +94,8 @@@ struct wg_callbac
  
      char    *node;
      char    *service;
 +    char    *protocol;
 +    _Bool   log_send_errors;
      char    *prefix;
      char    *postfix;
      char     escape_char;
@@@ -130,15 -118,10 +130,15 @@@ static int wg_send_buffer (struct wg_ca
      status = swrite (cb->sock_fd, cb->send_buf, strlen (cb->send_buf));
      if (status < 0)
      {
 -        char errbuf[1024];
 -        ERROR ("write_graphite plugin: send failed with status %zi (%s)",
 -                status, sstrerror (errno, errbuf, sizeof (errbuf)));
 +        const char *protocol = cb->protocol ? cb->protocol : WG_DEFAULT_PROTOCOL;
  
 +        if (cb->log_send_errors)
 +        {
 +            char errbuf[1024];
 +            ERROR ("write_graphite plugin: send to %s:%s (%s) failed with status %zi (%s)",
 +                    cb->node, cb->service, protocol,
 +                    status, sstrerror (errno, errbuf, sizeof (errbuf)));
 +        }
  
          close (cb->sock_fd);
          cb->sock_fd = -1;
@@@ -190,8 -173,9 +190,10 @@@ static int wg_callback_init (struct wg_
  
      const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
      const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE;
 +    const char *protocol = cb->protocol ? cb->protocol : WG_DEFAULT_PROTOCOL;
  
+     char connerr[1024] = "";
      if (cb->sock_fd > 0)
          return (0);
  
      ai_hints.ai_flags |= AI_ADDRCONFIG;
  #endif
      ai_hints.ai_family = AF_UNSPEC;
 -    ai_hints.ai_socktype = SOCK_STREAM;
 +
 +    if (0 == strcasecmp ("tcp", protocol))
 +        ai_hints.ai_socktype = SOCK_STREAM;
 +    else
 +        ai_hints.ai_socktype = SOCK_DGRAM;
  
      ai_list = NULL;
  
      status = getaddrinfo (node, service, &ai_hints, &ai_list);
      if (status != 0)
      {
 -        ERROR ("write_graphite plugin: getaddrinfo (%s, %s) failed: %s",
 -                node, service, gai_strerror (status));
 +        ERROR ("write_graphite plugin: getaddrinfo (%s, %s, %s) failed: %s",
 +                node, service, protocol, gai_strerror (status));
          return (-1);
      }
  
      {
          cb->sock_fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
                  ai_ptr->ai_protocol);
-         if (cb->sock_fd < 0)
+         if (cb->sock_fd < 0) {
+             char errbuf[1024];
+             snprintf (connerr, sizeof (connerr), "failed to open socket: %s",
+                     sstrerror (errno, errbuf, sizeof (errbuf)));
              continue;
+         }
  
          status = connect (cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
          if (status != 0)
          {
+             char errbuf[1024];
+             snprintf (connerr, sizeof (connerr), "failed to connect to remote "
+                     "host: %s", sstrerror (errno, errbuf, sizeof (errbuf)));
              close (cb->sock_fd);
              cb->sock_fd = -1;
              continue;
  
      if (cb->sock_fd < 0)
      {
-         char errbuf[1024];
+         if (connerr[0] == '\0')
+             /* this should not happen but try to get a message anyway */
+             sstrerror (errno, connerr, sizeof (connerr));
          c_complain (LOG_ERR, &cb->init_complaint,
-                 "write_graphite plugin: Connecting to %s:%s via %s failed. "
-                 "The last error was: %s", node, service, protocol,
-                 sstrerror (errno, errbuf, sizeof (errbuf)));
 -                "write_graphite plugin: Connecting to %s:%s failed. "
 -                "The last error was: %s", node, service, connerr);
++                  "write_graphite plugin: Connecting to %s:%s via %s failed. "
++                  "The last error was: %s", node, service, protocol, connerr);
          return (-1);
      }
      else
      {
          c_release (LOG_INFO, &cb->init_complaint,
 -                "write_graphite plugin: Successfully connected to %s:%s.",
 -                node, service);
 +                "write_graphite plugin: Successfully connected to %s:%s via %s.",
 +                node, service, protocol);
      }
  
      wg_reset_buffer (cb);
@@@ -279,7 -267,6 +289,7 @@@ static void wg_callback_free (void *dat
  
      sfree(cb->name);
      sfree(cb->node);
 +    sfree(cb->protocol);
      sfree(cb->service);
      sfree(cb->prefix);
      sfree(cb->postfix);
@@@ -360,10 -347,9 +370,10 @@@ static int wg_send_message (char const 
      cb->send_buf_fill += message_len;
      cb->send_buf_free -= message_len;
  
 -    DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"",
 +    DEBUG ("write_graphite plugin: [%s]:%s (%s) buf %zu/%zu (%.1f %%) \"%s\"",
              cb->node,
              cb->service,
 +            cb->protocol,
              cb->send_buf_fill, sizeof (cb->send_buf),
              100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)),
              message);
@@@ -453,7 -439,6 +463,7 @@@ static int wg_config_node (oconfig_item
      user_data_t user_data;
      char callback_name[DATA_MAX_NAME_LEN];
      int i;
 +    int status = 0;
  
      cb = malloc (sizeof (*cb));
      if (cb == NULL)
      cb->name = NULL;
      cb->node = NULL;
      cb->service = NULL;
 +    cb->protocol = NULL;
 +    cb->log_send_errors = WG_DEFAULT_LOG_SEND_ERRORS;
      cb->prefix = NULL;
      cb->postfix = NULL;
      cb->escape_char = WG_DEFAULT_ESCAPE;
      /* FIXME: Legacy configuration syntax. */
      if (strcasecmp ("Carbon", ci->key) != 0)
      {
 -        int status = cf_util_get_string (ci, &cb->name);
 +        status = cf_util_get_string (ci, &cb->name);
          if (status != 0)
          {
              wg_callback_free (cb);
              cf_util_get_string (child, &cb->node);
          else if (strcasecmp ("Port", child->key) == 0)
              cf_util_get_service (child, &cb->service);
 +        else if (strcasecmp ("Protocol", child->key) == 0)
 +        {
 +            cf_util_get_string (child, &cb->protocol);
 +
 +            if (strcasecmp ("UDP", cb->protocol) != 0 &&
 +                strcasecmp ("TCP", cb->protocol) != 0)
 +            {
 +                ERROR ("write_graphite plugin: Unknown protocol (%s)",
 +                        cb->protocol);
 +                status = -1;
 +            }
 +        }
 +        else if (strcasecmp ("LogSendErrors", child->key) == 0)
 +            cf_util_get_boolean (child, &cb->log_send_errors);
          else if (strcasecmp ("Prefix", child->key) == 0)
              cf_util_get_string (child, &cb->prefix);
          else if (strcasecmp ("Postfix", child->key) == 0)
          {
              ERROR ("write_graphite plugin: Invalid configuration "
                          "option: %s.", child->key);
 +            status = -1;
          }
 +
 +        if (status != 0)
 +            break;
 +    }
 +
 +    if (status != 0)
 +    {
 +        wg_callback_free (cb);
 +        return (status);
      }
  
      /* FIXME: Legacy configuration syntax. */
      if (cb->name == NULL)
 -        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s",
 +        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s/%s",
                  cb->node != NULL ? cb->node : WG_DEFAULT_NODE,
 -                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE);
 +                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE,
 +                cb->protocol != NULL ? cb->protocol : WG_DEFAULT_PROTOCOL);
      else
          ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s",
                  cb->name);