Merge branch 'collectd-5.6' into collectd-5.7
authorFlorian Forster <octo@collectd.org>
Thu, 8 Dec 2016 14:27:03 +0000 (15:27 +0100)
committerFlorian Forster <octo@collectd.org>
Thu, 8 Dec 2016 14:27:03 +0000 (15:27 +0100)
1  2 
src/collectd.conf.pod
src/smart.c
src/write_kafka.c

diff --combined src/collectd.conf.pod
@@@ -555,7 -555,6 +555,7 @@@ B<Synopsis:
   #   GraphiteEscapeChar "_"
   #   GraphiteSeparateInstances false
   #   GraphiteAlwaysAppendDS false
 + #   GraphitePreserveSeparator false
     </Publish>
  
     # Receive values from an AMQP broker
@@@ -730,12 -729,6 +730,12 @@@ If set to B<true>, append the name of t
  identifier. If set to B<false> (the default), this is only done when there is
  more than one DS.
  
 +=item B<GraphitePreserveSeparator> B<false>|B<true>
 +
 +If set to B<false> (the default) the C<.> (dot) character is replaced with
 +I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
 +is preserved, i.e. passed through.
 +
  =back
  
  =head2 Plugin C<apache>
@@@ -1121,13 -1114,6 +1121,13 @@@ When set to B<true>, the battery plugi
  and "remaining capacity") and B<degraded> (difference between "design capacity"
  and "last full capacity").
  
 +=item B<QueryStateFS> B<false>|B<true>
 +
 +When set to B<true>, the battery plugin will only read statistics
 +related to battery performance as exposed by StateFS at
 +/run/state. StateFS is used in Mer-based Sailfish OS, for
 +example.
 +
  =back
  
  =head2 Plugin C<bind>
@@@ -2383,67 -2369,6 +2383,67 @@@ Enabled by default, collects unknown (a
  
  =back
  
 +=head2 Plugin C<dpdkstat>
 +
 +The I<dpdkstat plugin> collects information about DPDK interfaces using the
 +extended NIC stats API in DPDK.
 +
 +B<Synopsis:>
 +
 + <Plugin "dpdkstat">
 +    Coremask "0x4"
 +    MemoryChannels "4"
 +    ProcessType "secondary"
 +    FilePrefix "rte"
 +    EnabledPortMask 0xffff
 +    PortName "interface1"
 +    PortName "interface2"
 + </Plugin>
 +
 +B<Options:>
 +
 +=over 4
 +
 +=item B<Coremask> I<Mask>
 +
 +A string containing an hexadecimal bit mask of the cores to run on. Note that
 +core numbering can change between platforms and should be determined beforehand.
 +
 +=item B<Memorychannels> I<Channels>
 +
 +A string containing a number of memory channels per processor socket.
 +
 +=item B<ProcessType> I<type>
 +
 +A string containing the type of DPDK process instance.
 +
 +=item B<FilePrefix> I<File>
 +
 +The prefix text used for hugepage filenames. The filename will be set to
 +/var/run/.<prefix>_config where prefix is what is passed in by the user.
 +
 +=item B<SocketMemory> I<MB>
 +
 +A string containing amount of Memory to allocate from hugepages on specific
 +sockets in MB
 +
 +=item B<EnabledPortMask> I<Mask>
 +
 +A hexidecimal bit mask of the DPDK ports which should be enabled. A mask
 +of 0x0 means that all ports will be disabled. A bitmask of all Fs means
 +that all ports will be enabled. This is an optional argument - default
 +is all ports enabled.
 +
 +=item B<PortName> I<Name>
 +
 +A string containing an optional name for the enabled DPDK ports. Each PortName
 +option should contain only one port name; specify as many PortName options as
 +desired. Default naming convention will be used if PortName is blank. If there
 +are less PortName options than there are enabled ports, the default naming
 +convention will be used for the additional ports.
 +
 +=back
 +
  =head2 Plugin C<email>
  
  =over 4
@@@ -2894,101 -2819,6 +2894,101 @@@ TCP-Port to connect to. Defaults to B<7
  
  =back
  
 +=head2 Plugin C<hugepages>
 +
 +To collect B<hugepages> information, collectd reads directories
 +"/sys/devices/system/node/*/hugepages" and
 +"/sys/kernel/mm/hugepages".
 +Reading of these directories can be disabled by the following
 +options (default is enabled).
 +
 +=over 4
 +
 +=item B<ReportPerNodeHP> B<true>|B<false>
 +
 +If enabled, information will be collected from the hugepage
 +counters in "/sys/devices/system/node/*/hugepages".
 +This is used to check the per-node hugepage statistics on
 +a NUMA system.
 +
 +=item B<ReportRootHP> B<true>|B<false>
 +
 +If enabled, information will be collected from the hugepage
 +counters in "/sys/kernel/mm/hugepages".
 +This can be used on both NUMA and non-NUMA systems to check
 +the overall hugepage statistics.
 +
 +=item B<ValuesPages> B<true>|B<false>
 +
 +Whether to report hugepages metrics in number of pages.
 +Defaults to B<true>.
 +
 +=item B<ValuesBytes> B<false>|B<true>
 +
 +Whether to report hugepages metrics in bytes.
 +Defaults to B<false>.
 +
 +=item B<ValuesPercentage> B<false>|B<true>
 +
 +Whether to report hugepages metrics as percentage.
 +Defaults to B<false>.
 +
 +=back
 +
 +=head2 Plugin C<intel_rdt>
 +
 +The I<intel_rdt> plugin collects information provided by monitoring features of
 +Intel Resource Director Technology (Intel(R) RDT) like Cache Monitoring
 +Technology (CMT), Memory Bandwidth Monitoring (MBM). These features provide
 +information about utilization of shared resources. CMT monitors last level cache
 +occupancy (LLC). MBM supports two types of events reporting local and remote
 +memory bandwidth. Local memory bandwidth (MBL) reports the bandwidth of
 +accessing memory associated with the local socket. Remote memory bandwidth (MBR)
 +reports the bandwidth of accessing the remote socket. Also this technology
 +allows to monitor instructions per clock (IPC).
 +Monitor events are hardware dependant. Monitoring capabilities are detected on
 +plugin initialization and only supported events are monitored.
 +
 +B<Synopsis:>
 +
 +  <Plugin "intel_rdt">
 +    Cores "0-2" "3,4,6" "8-10,15"
 +  </Plugin>
 +
 +B<Options:>
 +
 +=over 4
 +
 +=item B<Interval> I<seconds>
 +
 +The interval within which to retrieve statistics on monitored events in seconds.
 +For milliseconds divide the time by 1000 for example if the desired interval
 +is 50ms, set interval to 0.05. Due to limited capacity of counters it is not
 +recommended to set interval higher than 1 sec.
 +
 +=item B<Cores> I<cores groups>
 +
 +All events are reported on a per core basis. Monitoring of the events can be
 +configured for group of cores (aggregated statistics). This field defines groups
 +of cores on which to monitor supported events. The field is represented as list
 +of strings with core group values. Each string represents a list of cores in a
 +group. Allowed formats are:
 +    0,1,2,3
 +    0-10,20-18
 +    1,3,5-8,10,0x10-12
 +
 +If an empty string is provided as value for this field default cores
 +configuration is applied - a separate group is created for each core.
 +
 +=back
 +
 +B<Note:> By default global interval is used to retrieve statistics on monitored
 +events. To configure a plugin specific interval use B<Interval> option of the
 +intel_rdt <LoadPlugin> block. For milliseconds divide the time by 1000 for
 +example if the desired interval is 50ms, set interval to 0.05.
 +Due to limited capacity of counters it is not recommended to set interval higher
 +than 1 sec.
 +
  =head2 Plugin C<interface>
  
  =over 4
@@@ -3402,12 -3232,11 +3402,12 @@@ interpreted. For a description of matc
  
  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/>
 +L<http://memcached.org/>
  
   <Plugin "memcached">
     <Instance "name">
 -     Host "memcache.example.com"
 +     #Host "memcache.example.com"
 +     Address "127.0.0.1"
       Port 11211
     </Instance>
   </Plugin>
@@@ -3420,25 -3249,16 +3420,25 @@@ following options are allowed
  
  =item B<Host> I<Hostname>
  
 -Hostname to connect to. Defaults to B<127.0.0.1>.
 +Sets the B<host> field of dispatched values. Defaults to the global hostname
 +setting.
 +For backwards compatibility, values are also dispatched with the global
 +hostname when B<Host> is set to B<127.0.0.1> or B<localhost> and B<Address> is
 +not set.
 +
 +=item B<Address> I<Address>
 +
 +Hostname or IP to connect to. For backwards compatibility, defaults to the
 +value of B<Host> or B<127.0.0.1> if B<Host> is unset.
  
  =item B<Port> I<Port>
  
 -TCP-Port to connect to. Defaults to B<11211>.
 +TCP port to connect to. Defaults to B<11211>.
  
  =item B<Socket> I<Path>
  
  Connect to I<memcached> using the UNIX domain socket at I<Path>. If this
 -setting is given, the B<Host> and B<Port> settings are ignored.
 +setting is given, the B<Address> and B<Port> settings are ignored.
  
  =back
  
@@@ -4878,7 -4698,7 +4878,7 @@@ so the values will not loop
  =item B<ReportStats> B<true>|B<false>
  
  The network plugin cannot only receive and send statistics, it can also create
- statistics about itself. Collected data included the number of received and
+ statistics about itself. Collectd data included the number of received and
  sent octets and packets, the length of the receive queue and the number of
  values handled. When set to B<true>, the I<Network plugin> will make these
  statistics available. Defaults to B<false>.
@@@ -5879,7 -5699,7 +5879,7 @@@ values are made available through thos
  
  =item B<$1>
  
 -The timestamp of the queried value as a floating point number.
 +The timestamp of the queried value as an RFC 3339-formatted local time.
  
  =item B<$2>
  
@@@ -7089,15 -6909,6 +7089,15 @@@ user using (extended) regular expressio
          Type "counter"
          Instance "local_user"
        </Match>
 +      <Match>
 +        Regex "l=([0-9]*\\.[0-9]*)"
 +        <DSType "Distribution">
 +          Percentile 99
 +          Bucket 0 100
 +        </DSType>
 +        Type "latency"
 +        Instance "foo"
 +      </Match>
      </File>
    </Plugin>
  
@@@ -7160,13 -6971,6 +7160,13 @@@ Use the greatest number only
  
  Use the last number found.
  
 +=item B<GaugePersist>
 +
 +Use the last number found. The number is not reset at the end of an interval.
 +It is continously reported until another number is matched. This is intended
 +for cases in which only state changes are reported, for example a thermometer
 +that only reports the temperature when it changes.
 +
  =item B<CounterSet>
  
  =item B<DeriveSet>
@@@ -7196,74 -7000,14 +7196,74 @@@ Increase the internal counter by one. T
  not use the matched subexpression, but simply count the number of matched
  lines. Thus, you may use a regular expression without submatch in this case.
  
 +=item B<Distribution>
 +
 +Type to do calculations based on the distribution of values, primarily
 +calculating percentiles. This is primarily geared towards latency, but can be
 +used for other metrics as well. The range of values tracked with this setting
 +must be in the range (0–2^34) and can be fractional. Please note that neither
 +zero nor 2^34 are inclusive bounds, i.e. zero I<cannot> be handled by a
 +distribution.
 +
 +This option must be used together with the B<Percentile> and/or B<Bucket>
 +options.
 +
 +B<Synopsis:>
 +
 +  <DSType "Distribution">
 +    Percentile 99
 +    Bucket 0 100
 +  </DSType>
 +
 +=over 4
 +
 +=item B<Percentile> I<Percent>
 +
 +Calculate and dispatch the configured percentile, i.e. compute the value, so
 +that I<Percent> of all matched values are smaller than or equal to the computed
 +latency.
 +
 +Metrics are reported with the I<type> B<Type> (the value of the above option)
 +and the I<type instance> C<[E<lt>InstanceE<gt>-]E<lt>PercentE<gt>>.
 +
 +This option may be repeated to calculate more than one percentile.
 +
 +=item B<Bucket> I<lower_bound> I<upper_bound>
 +
 +Export the number of values (a C<DERIVE>) falling within the given range. Both,
 +I<lower_bound> and I<upper_bound> may be a fractional number, such as B<0.5>.
 +Each B<Bucket> option specifies an interval C<(I<lower_bound>,
 +I<upper_bound>]>, i.e. the range I<excludes> the lower bound and I<includes>
 +the upper bound. I<lower_bound> and I<upper_bound> may be zero, meaning no
 +lower/upper bound.
 +
 +To export the entire (0–inf) range without overlap, use the upper bound of the
 +previous range as the lower bound of the following range. In other words, use
 +the following schema:
 +
 +  Bucket   0   1
 +  Bucket   1   2
 +  Bucket   2   5
 +  Bucket   5  10
 +  Bucket  10  20
 +  Bucket  20  50
 +  Bucket  50   0
 +
 +Metrics are reported with the I<type> C<bucket> and the I<type instance>
 +C<E<lt>TypeE<gt>[-E<lt>InstanceE<gt>]-E<lt>lower_boundE<gt>_E<lt>upper_boundE<gt>>.
 +
 +This option may be repeated to calculate more than one rate.
 +
  =back
  
 -As you'd expect the B<Gauge*> types interpret the submatch as a floating point
 -number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types interpret
 -the submatch as an unsigned integer using L<strtoull(3)>. The B<Derive*> types
 -interpret the submatch as a signed integer using L<strtoll(3)>. B<CounterInc>
 -and B<DeriveInc> do not use the submatch at all and it may be omitted in this
 -case.
 +=back
 +
 +The B<Gauge*> and B<Distribution> types interpret the submatch as a floating
 +point number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types
 +interpret the submatch as an unsigned integer using L<strtoull(3)>. The
 +B<Derive*> types interpret the submatch as a signed integer using
 +L<strtoll(3)>. B<CounterInc> and B<DeriveInc> do not use the submatch at all
 +and it may be omitted in this case.
  
  =item B<Type> I<Type>
  
@@@ -7518,7 -7262,7 +7518,7 @@@ couple metrics: number of records, and 
  
  =item B<Host> I<Hostname/IP>
  
 -The hostname or ip which identifies the server.
 +The hostname or IP which identifies the server.
  Default: B<127.0.0.1>
  
  =item B<Port> I<Service/Port>
@@@ -7532,60 -7276,61 +7532,60 @@@ Default: B<1978
  =head2 Plugin C<turbostat>
  
  The I<Turbostat plugin> reads CPU frequency and C-state residency on modern
 -Intel processors by using the new Model Specific Registers.
 +Intel processors by using I<Model Specific Registers>.
  
  =over 4
  
  =item B<CoreCstates> I<Bitmask(Integer)>
  
 -Bitmask of the list of core C states supported by the processor.
 +Bit mask of the list of core C-states supported by the processor.
  This option should only be used if the automated detection fails.
 -Default value extracted from the cpu model and family.
 +Default value extracted from the CPU model and family.
  
  Currently supported C-states (by this plugin): 3, 6, 7
  
 -Example: (1<<3)+(1<<6)+(1<<7) = 392 for all states
 +B<Example:>
 +
 +  All states (3, 6 and 7):
 +  (1<<3) + (1<<6) + (1<<7) = 392
  
  =item B<PackageCstates> I<Bitmask(Integer)>
  
 -Bitmask of the list of pacages C states supported by the processor.
 -This option should only be used if the automated detection fails.
 -Default value extracted from the cpu model and family.
 +Bit mask of the list of packages C-states supported by the processor. This
 +option should only be used if the automated detection fails. Default value
 +extracted from the CPU model and family.
  
  Currently supported C-states (by this plugin): 2, 3, 6, 7, 8, 9, 10
  
 -Example: (1<<2)+(1<<3)+(1<<6)+(1<<7) = 396 for states 2, 3, 6 and 7
 -
 -=item B<SystemManagementInterrupt> I<true>|I<false>
 +B<Example:>
  
 -Boolean enabling the collection of the I/O System-Management Interrupt
 -counter'. This option should only be used if the automated detection
 -fails or if you want to disable this feature.
 +  States 2, 3, 6 and 7:
 +  (1<<2) + (1<<3) + (1<<6) + (1<<7) = 396
  
 -=item B<DigitalTemperatureSensor> I<true>|I<false>
 +=item B<SystemManagementInterrupt> I<true>|I<false>
  
 -Boolean enabling the collection of the temperature of each core.
 -This option should only be used if the automated detectionfails or
 -if you want to disable this feature.
 +Boolean enabling the collection of the I/O System-Management Interrupt counter.
 +This option should only be used if the automated detection fails or if you want
 +to disable this feature.
  
  =item B<DigitalTemperatureSensor> I<true>|I<false>
  
 -Boolean enabling the collection of the temperature of each package.
 -This option should only be used if the automated detectionfails or
 -if you want to disable this feature.
 +Boolean enabling the collection of the temperature of each core. This option
 +should only be used if the automated detection fails or if you want to disable
 +this feature.
  
  =item B<TCCActivationTemp> I<Temperature>
  
 -Thermal Control Circuit Activation Temperature of the installed
 -CPU. This temperature is used when collecting the temperature of
 -cores or packages. This option should only be used if the automated
 -detection fails. Default value extracted from B<MSR_IA32_TEMPERATURE_TARGET>
 +I<Thermal Control Circuit Activation Temperature> of the installed CPU. This
 +temperature is used when collecting the temperature of cores or packages. This
 +option should only be used if the automated detection fails. Default value
 +extracted from B<MSR_IA32_TEMPERATURE_TARGET>.
  
  =item B<RunningAveragePowerLimit> I<Bitmask(Integer)>
  
 -Bitmask of the list of elements to be thermally monitored. This option
 -should only be used if the automated detection fails or if you want to
 -disable some collections. The different bits of this bitmask accepted
 -by this plugin are:
 +Bit mask of the list of elements to be thermally monitored. This option should
 +only be used if the automated detection fails or if you want to disable some
 +collections. The different bits of this bit mask accepted by this plugin are:
  
  =over 4
  
  
  =back
  
 +=item B<LogicalCoreNames> I<true>|I<false>
 +
 +Boolean enabling the use of logical core numbering for per core statistics.
 +When enabled, C<cpuE<lt>nE<gt>> is used as plugin instance, where I<n> is a
 +sequential number assigned by the kernel. Otherwise, C<coreE<lt>nE<gt>> is used
 +where I<n> is the n-th core of the socket, causing name conflicts when there is
 +more than one socket.
 +
  =back
  
  =head2 Plugin C<unixsock>
@@@ -7888,49 -7625,6 +7888,49 @@@ Example
  Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
  will be collected.
  
 +=item B<BlockDeviceFormat> B<target>|B<source>
 +
 +If I<BlockDeviceFormat> is set to B<target>, the default, then the device name
 +seen by the guest will be used for reporting metrics. 
 +This corresponds to the C<E<lt>targetE<gt>> node in the XML definition of the
 +domain.
 +
 +If I<BlockDeviceFormat> is set to B<source>, then metrics will be reported
 +using the path of the source, e.g. an image file.
 +This corresponds to the C<E<lt>sourceE<gt>> node in the XML definition of the
 +domain.
 +
 +B<Example:>
 +
 +If the domain XML have the following device defined:
 +
 +  <disk type='block' device='disk'>
 +    <driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
 +    <source dev='/var/lib/libvirt/images/image1.qcow2'/>
 +    <target dev='sda' bus='scsi'/>
 +    <boot order='2'/>
 +    <address type='drive' controller='0' bus='0' target='0' unit='0'/>
 +  </disk>
 +
 +Setting C<BlockDeviceFormat target> will cause the I<type instance> to be set
 +to C<sda>.
 +Setting C<BlockDeviceFormat source> will cause the I<type instance> to be set
 +to C<var_lib_libvirt_images_image1.qcow2>.
 +
 +=item B<BlockDeviceFormatBasename> B<false>|B<true>
 +
 +The B<BlockDeviceFormatBasename> controls whether the full path or the
 +L<basename(1)> of the source is being used as the I<type instance> when
 +B<BlockDeviceFormat> is set to B<source>. Defaults to B<false>.
 +
 +B<Example:>
 +
 +Assume the device path (source tag) is C</var/lib/libvirt/images/image1.qcow2>.
 +Setting C<BlockDeviceFormatBasename false> will cause the I<type instance> to
 +be set to C<var_lib_libvirt_images_image1.qcow2>.
 +Setting C<BlockDeviceFormatBasename true> will cause the I<type instance> to be
 +set to C<image1.qcow2>.
 +
  =item B<HostnameFormat> B<name|uuid|hostname|...>
  
  When the virt plugin logs data, it sets the hostname of the collected data
@@@ -8097,38 -7791,6 +8097,38 @@@ If set to B<true>, append the name of t
  identifier. If set to B<false> (the default), this is only done when there is
  more than one DS.
  
 +=item B<PreserveSeparator> B<false>|B<true>
 +
 +If set to B<false> (the default) the C<.> (dot) character is replaced with
 +I<EscapeCharacter>. Otherwise, if set to B<true>, the C<.> (dot) character
 +is preserved, i.e. passed through.
 +
 +=item B<DropDuplicateFields> B<false>|B<true>
 +
 +If set to B<true>, detect and remove duplicate components in Graphite metric
 +names. For example, the metric name  C<host.load.load.shortterm> will
 +be shortened to C<host.load.shortterm>.
 +
 +=back
 +
 +=head2 Plugin C<write_log>
 +
 +The C<write_log> plugin writes metrics as INFO log messages.
 +
 +This plugin supports two output formats: I<Graphite> and I<JSON>.
 +
 +Synopsis:
 +
 + <Plugin write_log>
 +   Format Graphite
 + </Plugin>
 +
 +=over 4
 +
 +=item B<Format> I<Format>
 +
 +The output format to use. Can be one of C<Graphite> or C<JSON>.
 +
  =back
  
  =head2 Plugin C<write_tsdb>
@@@ -8237,41 -7899,6 +8237,41 @@@ want to use authentication all three fi
  
  =back
  
 +=head2 Plugin C<write_prometheus>
 +
 +The I<write_prometheus plugin> implements a tiny webserver that can be scraped
 +using I<Prometheus>.
 +
 +B<Options:>
 +
 +=over 4
 +
 +=item B<Port> I<Port>
 +
 +Port the embedded webserver should listen on. Defaults to B<9103>.
 +
 +=item B<StalenessDelta> I<Seconds>
 +
 +Time in seconds after which I<Prometheus> considers a metric "stale" if it
 +hasn't seen any update for it. This value must match the setting in Prometheus.
 +It defaults to B<300> seconds (5 minutes), same as Prometheus.
 +
 +B<Background:>
 +
 +I<Prometheus> has a global setting, C<StalenessDelta>, which controls after
 +which time a metric without updates is considered "stale". This setting
 +effectively puts an upper limit on the interval in which metrics are reported.
 +
 +When the I<write_prometheus plugin> encounters a metric with an interval
 +exceeding this limit, it will inform you, the user, and provide the metric to
 +I<Prometheus> B<without> a timestamp. That causes I<Prometheus> to consider the
 +metric "fresh" each time it is scraped, with the time of the scrape being
 +considered the time of the update. The result is that there appear more
 +datapoints in I<Prometheus> than were actually created, but at least the metric
 +doesn't disappear periodically.
 +
 +=back
 +
  =head2 Plugin C<write_http>
  
  This output plugin submits values to an HTTP server using POST requests and
@@@ -8505,18 -8132,6 +8505,18 @@@ path component, for example C<host.cpu.
  default), the plugin and plugin instance (and likewise the type and type
  instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
  
 +=item B<GraphiteAlwaysAppendDS> B<true>|B<false>
 +
 +If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
 +identifier. If set to B<false> (the default), this is only done when there is
 +more than one DS.
 +
 +=item B<GraphitePreserveSeparator> B<false>|B<true>
 +
 +If set to B<false> (the default) the C<.> (dot) character is replaced with
 +I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
 +is preserved, i.e. passed through.
 +
  =item B<StoreRates> B<true>|B<false>
  
  If set to B<true> (the default), convert counter values to rates. If set to
@@@ -9451,8 -9066,6 +9451,8 @@@ Available options
  
  =item B<TypeInstance> I<Regex>
  
 +=item B<MetaData> I<String> I<Regex>
 +
  Match values where the given regular expressions match the various fields of
  the identifier of a value. If multiple regular expressions are given, B<all>
  regexen must match for a value to match.
@@@ -9741,10 -9354,6 +9741,10 @@@ Available options
  
  =item B<TypeInstance> I<Regex> I<Replacement>
  
 +=item B<MetaData> I<String> I<Regex> I<Replacement>
 +
 +=item B<DeleteMetaData> I<String> I<Regex>
 +
  Match the appropriate field with the given regular expression I<Regex>. If the
  regular expression matches, that part that matches is replaced with
  I<Replacement>. If multiple places of the input buffer match a given regular
@@@ -9783,37 -9392,9 +9783,37 @@@ Available options
  
  =item B<MetaData> I<String> I<String>
  
 -Set the appropriate field to the given string. The strings for plugin instance
 -and type instance may be empty, the strings for host and plugin may not be
 -empty. It's currently not possible to set the type of a value this way.
 +Set the appropriate field to the given string. The strings for plugin instance,
 +type instance, and meta data may be empty, the strings for host and plugin may
 +not be empty. It's currently not possible to set the type of a value this way.
 +
 +The following placeholders will be replaced by an appropriate value:
 +
 +=over 4
 +
 +=item B<%{host}>
 +
 +=item B<%{plugin}>
 +
 +=item B<%{plugin_instance}>
 +
 +=item B<%{type}>
 +
 +=item B<%{type_instance}>
 +
 +These placeholders are replaced by the identifier field of the same name.
 +
 +=item B<%{meta:>I<name>B<}>
 +
 +These placeholders are replaced by the meta data value with the given name.
 +
 +=back
 +
 +Please note that these placeholders are B<case sensitive>!
 +
 +=item B<DeleteMetaData> I<String>
 +
 +Delete the named meta data field.
  
  =back
  
diff --combined src/smart.c
@@@ -70,10 -70,14 +70,10 @@@ static int smart_config(const char *key
  
  static void smart_submit(const char *dev, const char *type,
                           const char *type_inst, double value) {
 -  value_t values[1];
    value_list_t vl = VALUE_LIST_INIT;
  
 -  values[0].gauge = value;
 -
 -  vl.values = values;
 +  vl.values = &(value_t){.gauge = value};
    vl.values_len = 1;
 -  sstrncpy(vl.host, hostname_g, sizeof(vl.host));
    sstrncpy(vl.plugin, "smart", sizeof(vl.plugin));
    sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
    sstrncpy(vl.type, type, sizeof(vl.type));
    plugin_dispatch_values(&vl);
  }
  
- static void smart_handle_disk_attribute(SkDisk *d,
-                                         const SkSmartAttributeParsedData *a,
-                                         void *userdata) {
-   const char *dev = userdata;
+ static void handle_attribute(SkDisk *d, const SkSmartAttributeParsedData *a,
+                              void *userdata) {
+   char const *name = userdata;
 -  value_t values[4];
 -  value_list_t vl = VALUE_LIST_INIT;
  
    if (!a->current_value_valid || !a->worst_value_valid)
      return;
 -  values[0].gauge = a->current_value;
 -  values[1].gauge = a->worst_value;
 -  values[2].gauge = a->threshold_valid ? a->threshold : 0;
 -  values[3].gauge = a->pretty_value;
 +
 +  value_list_t vl = VALUE_LIST_INIT;
 +  value_t values[] = {
 +      {.gauge = a->current_value},
 +      {.gauge = a->worst_value},
 +      {.gauge = a->threshold_valid ? a->threshold : 0},
 +      {.gauge = a->pretty_value},
 +  };
  
    vl.values = values;
 -  vl.values_len = 4;
 -  sstrncpy(vl.host, hostname_g, sizeof(vl.host));
 +  vl.values_len = STATIC_ARRAY_SIZE(values);
    sstrncpy(vl.plugin, "smart", sizeof(vl.plugin));
-   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
+   sstrncpy(vl.plugin_instance, name, sizeof(vl.plugin_instance));
    sstrncpy(vl.type, "smart_attribute", sizeof(vl.type));
    sstrncpy(vl.type_instance, a->name, sizeof(vl.type_instance));
  
      notification_t notif = {NOTIF_WARNING,     cdtime(), "",  "", "smart", "",
                              "smart_attribute", "",       NULL};
      sstrncpy(notif.host, hostname_g, sizeof(notif.host));
-     sstrncpy(notif.plugin_instance, dev, sizeof(notif.plugin_instance));
+     sstrncpy(notif.plugin_instance, name, sizeof(notif.plugin_instance));
      sstrncpy(notif.type_instance, a->name, sizeof(notif.type_instance));
      ssnprintf(notif.message, sizeof(notif.message),
                "attribute %s is below allowed threshold (%d < %d)", a->name,
    }
  }
  
- static void smart_handle_disk(const char *dev, const char *serial) {
-   SkDisk *d = NULL;
-   SkBool awake = FALSE;
+ static void smart_read_disk(SkDisk *d, char const *name) {
    SkBool available = FALSE;
-   const char *shortname;
-   const SkSmartParsedData *spd;
-   uint64_t poweron, powercycles, badsectors, temperature;
-   if (use_serial && serial) {
-     shortname = serial;
-   } else {
-     shortname = strrchr(dev, '/');
-     if (!shortname)
-       return;
-     shortname++;
-   }
-   if (ignorelist_match(ignorelist, shortname) != 0) {
-     DEBUG("smart plugin: ignoring %s.", dev);
-     return;
-   }
-   DEBUG("smart plugin: checking SMART status of %s.", dev);
-   if (sk_disk_open(dev, &d) < 0) {
-     ERROR("smart plugin: unable to open %s.", dev);
-     return;
-   }
    if (sk_disk_identify_is_available(d, &available) < 0 || !available) {
-     DEBUG("smart plugin: disk %s cannot be identified.", dev);
-     goto end;
+     DEBUG("smart plugin: disk %s cannot be identified.", name);
+     return;
    }
    if (sk_disk_smart_is_available(d, &available) < 0 || !available) {
-     DEBUG("smart plugin: disk %s has no SMART support.", dev);
-     goto end;
+     DEBUG("smart plugin: disk %s has no SMART support.", name);
+     return;
    }
    if (!ignore_sleep_mode) {
+     SkBool awake = FALSE;
      if (sk_disk_check_sleep_mode(d, &awake) < 0 || !awake) {
-       DEBUG("smart plugin: disk %s is sleeping.", dev);
-       goto end;
+       DEBUG("smart plugin: disk %s is sleeping.", name);
+       return;
      }
    }
    if (sk_disk_smart_read_data(d) < 0) {
-     ERROR("smart plugin: unable to get SMART data for disk %s.", dev);
-     goto end;
+     ERROR("smart plugin: unable to get SMART data for disk %s.", name);
+     return;
    }
-   if (sk_disk_smart_parse(d, &spd) < 0) {
-     ERROR("smart plugin: unable to parse SMART data for disk %s.", dev);
-     goto end;
+   if (sk_disk_smart_parse(d, &(SkSmartParsedData const *){NULL}) < 0) {
+     ERROR("smart plugin: unable to parse SMART data for disk %s.", name);
+     return;
    }
  
    /* Get some specific values */
-   if (sk_disk_smart_get_power_on(d, &poweron) < 0) {
-     WARNING("smart plugin: unable to get milliseconds since power on for %s.",
-             dev);
-   } else
-     smart_submit(shortname, "smart_poweron", "", poweron / 1000.);
-   if (sk_disk_smart_get_power_cycle(d, &powercycles) < 0) {
-     WARNING("smart plugin: unable to get number of power cycles for %s.", dev);
-   } else
-     smart_submit(shortname, "smart_powercycles", "", powercycles);
-   if (sk_disk_smart_get_bad(d, &badsectors) < 0) {
-     WARNING("smart plugin: unable to get number of bad sectors for %s.", dev);
-   } else
-     smart_submit(shortname, "smart_badsectors", "", badsectors);
-   if (sk_disk_smart_get_temperature(d, &temperature) < 0) {
-     WARNING("smart plugin: unable to get temperature for %s.", dev);
-   } else
-     smart_submit(shortname, "smart_temperature", "",
-                  temperature / 1000. - 273.15);
+   uint64_t value;
+   if (sk_disk_smart_get_power_on(d, &value) >= 0)
+     smart_submit(name, "smart_poweron", "", ((gauge_t)value) / 1000.);
+   else
+     DEBUG("smart plugin: unable to get milliseconds since power on for %s.",
+           name);
+   if (sk_disk_smart_get_power_cycle(d, &value) >= 0)
+     smart_submit(name, "smart_powercycles", "", (gauge_t)value);
+   else
+     DEBUG("smart plugin: unable to get number of power cycles for %s.", name);
+   if (sk_disk_smart_get_bad(d, &value) >= 0)
+     smart_submit(name, "smart_badsectors", "", (gauge_t)value);
+   else
+     DEBUG("smart plugin: unable to get number of bad sectors for %s.", name);
+   if (sk_disk_smart_get_temperature(d, &value) >= 0)
+     smart_submit(name, "smart_temperature", "",
+                  ((gauge_t)value) / 1000. - 273.15);
+   else
+     DEBUG("smart plugin: unable to get temperature for %s.", name);
  
    /* Grab all attributes */
-   if (sk_disk_smart_parse_attributes(d, smart_handle_disk_attribute,
-                                      (char *)shortname) < 0) {
-     ERROR("smart plugin: unable to handle SMART attributes for %s.", dev);
+   if (sk_disk_smart_parse_attributes(d, handle_attribute, (void *)name) < 0) {
+     ERROR("smart plugin: unable to handle SMART attributes for %s.", name);
+   }
+ }
+ static void smart_handle_disk(const char *dev, const char *serial) {
+   SkDisk *d = NULL;
+   const char *name;
+   if (use_serial && serial) {
+     name = serial;
+   } else {
+     name = strrchr(dev, '/');
+     if (!name)
+       return;
+     name++;
+   }
+   if (ignorelist_match(ignorelist, name) != 0) {
+     DEBUG("smart plugin: ignoring %s.", dev);
+     return;
+   }
+   DEBUG("smart plugin: checking SMART status of %s.", dev);
+   if (sk_disk_open(dev, &d) < 0) {
+     ERROR("smart plugin: unable to open %s.", dev);
+     return;
    }
  
- end:
+   smart_read_disk(d, name);
    sk_disk_free(d);
  }
  
diff --combined src/write_kafka.c
@@@ -31,6 -31,7 +31,7 @@@
  #include "utils_cmd_putval.h"
  #include "utils_format_graphite.h"
  #include "utils_format_json.h"
+ #include "utils_random.h"
  
  #include <errno.h>
  #include <librdkafka/rdkafka.h>
@@@ -88,7 -89,7 +89,7 @@@ static uint32_t kafka_hash(const char *
  #define KAFKA_RANDOM_KEY_BUFFER                                                \
    (char[KAFKA_RANDOM_KEY_SIZE]) { "" }
  static char *kafka_random_key(char buffer[static KAFKA_RANDOM_KEY_SIZE]) {
-   ssnprintf(buffer, KAFKA_RANDOM_KEY_SIZE, "%08lX", (unsigned long)mrand48());
+   ssnprintf(buffer, KAFKA_RANDOM_KEY_SIZE, "%08" PRIX32, cdrand_u());
    return buffer;
  }
  
@@@ -185,10 -186,9 +186,10 @@@ static int kafka_write(const data_set_
  
    switch (ctx->format) {
    case KAFKA_FORMAT_COMMAND:
 -    status = create_putval(buffer, sizeof(buffer), ds, vl);
 +    status = cmd_create_putval(buffer, sizeof(buffer), ds, vl);
      if (status != 0) {
 -      ERROR("write_kafka plugin: create_putval failed with status %i.", status);
 +      ERROR("write_kafka plugin: cmd_create_putval failed with status %i.",
 +            status);
        return status;
      }
      blen = strlen(buffer);
@@@ -370,10 -370,6 +371,10 @@@ static void kafka_config_topic(rd_kafka
        status = cf_util_get_flag(child, &tctx->graphite_flags,
                                  GRAPHITE_ALWAYS_APPEND_DS);
  
 +    } else if (strcasecmp("GraphitePreserveSeparator", child->key) == 0) {
 +      status = cf_util_get_flag(child, &tctx->graphite_flags,
 +                                GRAPHITE_PRESERVE_SEPARATOR);
 +
      } else if (strcasecmp("GraphitePrefix", child->key) == 0) {
        status = cf_util_get_string(child, &tctx->prefix);
      } else if (strcasecmp("GraphitePostfix", child->key) == 0) {
    ssnprintf(callback_name, sizeof(callback_name), "write_kafka/%s",
              tctx->topic_name);
  
 -  user_data_t ud = {.data = tctx, .free_func = kafka_topic_context_free};
 -
 -  status = plugin_register_write(callback_name, kafka_write, &ud);
 +  status = plugin_register_write(
 +      callback_name, kafka_write,
 +      &(user_data_t){
 +          .data = tctx, .free_func = kafka_topic_context_free,
 +      });
    if (status != 0) {
      WARNING("write_kafka plugin: plugin_register_write (\"%s\") "
              "failed with status %i.",