#<Plugin "intel_rdt">
# Cores "0-2"
+ # Processes "sshd"
#</Plugin>
#<Plugin interface>
# Connection "xen:///"
# RefreshInterval 60
# Domain "name"
+ # ReportBlockDevices true
+ # ReportNetworkInterfaces true
# BlockDevice "name:device"
# BlockDeviceFormat target
# BlockDeviceFormatBasename false
# InterfaceDevice "name:device"
# IgnoreSelected false
# HostnameFormat name
+ # HostnameMetadataXPath "/instance/name/text()"
+ # HostnameMetadataNS "http://openstack.org/xmlns/libvirt/nova/1.0"
# InterfaceFormat name
# PluginInstanceFormat name
# Instances 1
- # ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpupin"
+ # ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpupin disk_physical disk_allocation disk_capacity"
# PersistentNotification false
#</Plugin>
# SeparateInstances false
# PreserveSeparator false
# DropDuplicateFields false
+# ReverseHost false
# </Node>
#</Plugin>
<Plugin "intel_rdt">
Cores "0-2" "3,4,6" "8-10,15"
+ Processes "sshd,qemu-system-x86" "bash"
</Plugin>
B<Options:>
=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:
+ 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.
+ =item B<Processes> I<process names groups>
+
+ Monitoring of the events can be configured for group of processes
+ (aggregated statistics). This field defines groups of processes on which to
+ monitor supported events. The field is represented as list of strings with
+ process names group values. Each string represents a list of processes in a
+ group. Allowed format is:
+ sshd,bash,qemu
+
=back
B<Note:> By default global interval is used to retrieve statistics on monitored
For Modbus/RTU, specifies the baud rate of the serial device.
Note, connections currently support only 8/N/1.
+ =item B<UARTType> I<UARTType>
+
+ For Modbus/RTU, specifies the type of the serial device.
+ RS232, RS422 and RS485 are supported. Defaults to RS232.
+ Available only on Linux systems with libmodbus>=2.9.4.
+
=item B<Interval> I<Interval>
Sets the interval (in seconds) in which the values will be collected from this
namely octets, packets, and errors. These statistics are collected by
the C<interface> plugin, too, so using both at the same time is no benefit.
- When configured with B<VerboseInterface> all counters B<except> the basic ones,
- so that no data needs to be collected twice if you use the C<interface> plugin.
+ When configured with B<VerboseInterface> all counters B<except> the basic ones
+ will be collected, so that no data needs to be collected twice if you use the
+ C<interface> plugin.
This includes dropped packets, received multicast packets, collisions and a
whole zoo of differentiated RX and TX errors. You can try the following command
to get an idea of what awaits you:
Address "127.0.0.1"
Socket "/var/run/openvswitch/db.sock"
Bridges "br0" "br_ext"
+ InterfaceStats false
</Plugin>
The plugin provides the following configuration options:
Default: empty (monitor all bridges)
+ =item B<InterfaceStats> B<false>|B<true>
+
+ Indicates that the plugin should gather statistics for individual interfaces
+ in addition to ports. This can be useful when monitoring an OVS setup with
+ bond ports, where you might wish to know individual statistics for the
+ interfaces included in the bonds. Defaults to B<false>.
+
=back
=head2 Plugin C<pcie_errors>
=item B<GaugeAverage>
- Calculate the average.
+ Calculate the average of all values matched during the interval.
=item B<GaugeMin>
- Use the smallest number only.
+ Report the smallest value matched during the interval.
=item B<GaugeMax>
- Use the greatest number only.
+ Report the greatest value matched during the interval.
=item B<GaugeLast>
- Use the last number found.
+ Report the last value matched during the interval.
=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.
+ Report the last matching value. The metric is I<not> reset to C<NaN> at the end
+ of an interval. It is continuously reported until another value 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>
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.
+ B<GaugeInc> is reset to I<zero> after every read, unlike other B<Gauge*>
+ metrics which are reset to C<NaN>.
+
=item B<Distribution>
Type to do calculations based on the distribution of values, primarily
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.
+ L<strtoll(3)>. B<CounterInc>, B<DeriveInc> and B<GaugeInc> do not use the
+ submatch at all and it may be omitted in this case.
+
+ The B<Gauge*> types, unless noted otherwise, are reset to C<NaN> after being
+ reported. In other words, B<GaugeAverage> reports the average of all values
+ matched since the last metric was reported (or C<NaN> if there was no match).
=item B<Type> I<Type>
The default is to collect statistics for all domains and all their devices.
- Example:
+ B<Note:> B<BlockDevice> and B<InterfaceDevice> options are related to
+ corresponding B<*Format> options. Specifically, B<BlockDevice> filtering depends
+ on B<BlockDeviceFormat> setting - if user wants to filter block devices by
+ 'target' name then B<BlockDeviceFormat> option has to be set to 'target' and
+ B<BlockDevice> option must be set to a valid block device target
+ name("/:hdb/"). Mixing formats and filter values from different worlds (i.e.,
+ using 'target' name as B<BlockDevice> value with B<BlockDeviceFormat> set to
+ 'source') may lead to unexpected results (all devices filtered out or all
+ visible, depending on the value of B<IgnoreSelected> option).
+ Similarly, option B<InterfaceDevice> is related to B<InterfaceFormat> setting
+ (i.e., when user wants to use MAC address as a filter then B<InterfaceFormat>
+ has to be set to 'address' - using wrong type here may filter out all of the
+ interfaces).
+
+ B<Example 1:>
+
+ Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
+ will be collected:
BlockDevice "/:hdb/"
IgnoreSelected "true"
+ BlockDeviceFormat "target"
- Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
- will be collected.
+ B<Example 2:>
+
+ Collect metrics only for block device on 'baremetal0' domain when its
+ 'source' matches given path:
+
+ BlockDevice "baremetal0:/var/lib/libvirt/images/baremetal0.qcow2"
+ BlockDeviceFormat source
+
+ As you can see it is possible to filter devices/interfaces using
+ various formats - for block devices 'target' or 'source' name can be
+ used. Interfaces can be filtered using 'name', 'address' or 'number'.
+
+ B<Example 3:>
+
+ Collect metrics only for domains 'baremetal0' and 'baremetal1' and
+ ignore any other domain:
+
+ Domain "baremetal0"
+ Domain "baremetal1"
+
+ It is possible to filter multiple block devices/domains/interfaces by
+ adding multiple filtering entries in separate lines.
=item B<BlockDeviceFormat> B<target>|B<source>
Setting C<BlockDeviceFormat source> will cause the I<type instance> to be set
to C<var_lib_libvirt_images_image1.qcow2>.
+ B<Note:> this option determines also what field will be used for
+ filtering over block devices (filter value in B<BlockDevice>
+ will be applied to target or source). More info about filtering
+ block devices can be found in the description of B<BlockDevice>.
+
=item B<BlockDeviceFormatBasename> B<false>|B<true>
The B<BlockDeviceFormatBasename> controls whether the full path or the
Setting C<BlockDeviceFormatBasename true> will cause the I<type instance> to be
set to C<image1.qcow2>.
- =item B<HostnameFormat> B<name|uuid|hostname|...>
+ =item B<HostnameFormat> B<name|uuid|hostname|metadata...>
When the virt plugin logs data, it sets the hostname of the collected data
according to this setting. The default is to use the guest name as provided by
useful on its own because all guests will appear to have the same name. This is
useful in conjunction with B<PluginInstanceFormat> though.
+ B<metadata> means use information from guest's metadata. Use
+ B<HostnameMetadataNS> and B<HostnameMetadataXPath> to localize this information.
+
You can also specify combinations of these fields. For example B<name uuid>
means to concatenate the guest name and UUID (with a literal colon character
between, thus I<"foo:1234-1234-1234-1234">).
characters. In case when combination of fields exceeds 62 characters,
hostname will be truncated without a warning.
- =item B<InterfaceFormat> B<name>|B<address>
+ =item B<InterfaceFormat> B<name>|B<address>|B<number>
When the virt plugin logs interface data, it sets the name of the collected
data according to this setting. The default is to use the path as provided by
B<address> means use the interface's mac address. This is useful since the
interface path might change between reboots of a guest or across migrations.
- =item B<PluginInstanceFormat> B<name|uuid|none>
+ B<number> means use the interface's number in guest.
+
+ B<Note:> this option determines also what field will be used for
+ filtering over interface device (filter value in B<InterfaceDevice>
+ will be applied to name, address or number). More info about filtering
+ interfaces can be found in the description of B<InterfaceDevice>.
+
+ =item B<PluginInstanceFormat> B<name|uuid|metadata|none>
When the virt plugin logs data, it sets the plugin_instance of the collected
data according to this setting. The default is to not set the plugin_instance.
B<name> means use the guest's name as provided by the hypervisor.
B<uuid> means use the guest's UUID.
+ B<metadata> means use information from guest's metadata.
You can also specify combinations of the B<name> and B<uuid> fields.
For example B<name uuid> means to concatenate the guest name and UUID
(with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
- =item B<Instances> B<integer>
+ =item B<HostnameMetadataNS> B<string>
- How many read instances you want to use for this plugin. The default is one,
- and the sensible setting is a multiple of the B<ReadThreads> value.
- If you are not sure, just use the default setting.
+ When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+ selects in which metadata namespace we will pick the hostname. The default is
+ I<http://openstack.org/xmlns/libvirt/nova/1.0>.
+
+ =item B<HostnameMetadataXPath> B<string>
+
+ When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+ describes where the hostname is located in the libvirt metadata. The default is
+ I</instance/name/text()>.
+
+ =item B<ReportBlockDevices> B<true>|B<false>
+
+ Enabled by default. Allows to disable stats reporting of block devices for
+ whole plugin.
+
+ =item B<ReportNetworkInterfaces> B<true>|B<false>
+
+ Enabled by default. Allows to disable stats reporting of network interfaces for
+ whole plugin.
=item B<ExtraStats> B<string>
=item B<vcpupin>: report pinning of domain VCPUs to host physical CPUs.
+ =item B<disk_physical>: report 'disk_physical' statistic for disk device.
+ B<Note>: This statistic is only reported for disk devices with 'source'
+ property available.
+
+ =item B<disk_allocation>: report 'disk_allocation' statistic for disk device.
+ B<Note>: This statistic is only reported for disk devices with 'source'
+ property available.
+
+ =item B<disk_capacity>: report 'disk_capacity' statistic for disk device.
+ B<Note>: This statistic is only reported for disk devices with 'source'
+ property available.
+
=back
=item B<PersistentNotification> B<true>|B<false>
+
Override default configuration to only send notifications when there is a change
in the lifecycle state of a domain. When set to true notifications will be sent
for every read cycle. Default is false. Does not affect the stats being
dispatched.
+ =item B<Instances> B<integer>
+
+ How many read instances you want to use for this plugin. The default is one,
+ and the sensible setting is a multiple of the B<ReadThreads> value.
+
+ This option is only useful when domains are specially tagged.
+ If you are not sure, just use the default setting.
+
+ The reader instance will only query the domains with attached matching tag.
+ Tags should have the form of 'virt-X' where X is the reader instance number,
+ starting from 0.
+
+ The special-purpose reader instance #0, guaranteed to be always present,
+ will query all the domains with missing or unrecognized tag, so no domain will
+ ever be left out.
+
+ Domain tagging is done with a custom attribute in the libvirt domain metadata
+ section. Value is selected by an XPath I</domain/metadata/ovirtmap/tag/text()>
+ expression in the I<http://ovirt.org/ovirtmap/tag/1.0> namespace.
+ (XPath and namespace values are not configurable yet).
+
+ Tagging could be used by management applications to evenly spread the
+ load among the reader threads, or to pin on the same threads all
+ the libvirt domains which use the same shared storage, to minimize
+ the disruption in presence of storage outages.
+
=back
=head2 Plugin C<vmem>
LogSendErrors true
Prefix "collectd"
UseTags false
+ ReverseHost false
</Node>
</Plugin>
Default value: B<false>.
+=item B<ReverseHost> B<false>|B<true>
+
+If set to B<true>, the (dot separated) parts of the B<host> field of the
+I<value list> will be rewritten in reverse order. The rewrite happens I<before>
+special characters are replaced with the B<EscapeCharacter>.
+
+This option might be convenient if the metrics are presented with Graphite in a
+DNS like tree structure (probably without replacing dots in hostnames).
+
+Example:
+ Hostname "node3.cluster1.example.com"
+ LoadPlugin "cpu"
+ LoadPlugin "write_graphite"
+ <Plugin "write_graphite">
+ <Node "graphite.example.com">
+ EscapeCharacter "."
+ ReverseHost true
+ </Node>
+ </Plugin>
+
+ result on the wire: com.example.cluster1.node3.cpu-0.cpu-idle 99.900993 1543010932
+
+Default value: B<false>.
+
=back
=head2 Plugin C<write_log>
=head1 IGNORELISTS
B<Ignorelists> are a generic framework to either ignore some metrics or report
- specific metircs only. Plugins usually provide one or more options to specify
+ specific metrics only. Plugins usually provide one or more options to specify
the items (mounts points, devices, ...) and the boolean option
C<IgnoreSelected>.
--- /dev/null
- gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
+ /**
+ * collectd - src/utils_format_graphite.c
+ * Copyright (C) 2012 Thomas Meson
+ * Copyright (C) 2012 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Thomas Meson <zllak at hycik.org>
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+ #include "collectd.h"
+
+ #include "plugin.h"
+ #include "utils/common/common.h"
+
+ #include "utils/format_graphite/format_graphite.h"
+ #include "utils_cache.h"
+
+ #define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r"
+
+ /* Utils functions to format data sets in graphite format.
+ * Largely taken from write_graphite.c as it remains the same formatting */
+
++/* helper function for reverse_hostname */
++void reverse_string(char *r_host, int len) {
++ char t;
++ for (int i = 0; i < len / 2; i++) {
++ int j = len - i - 1;
++ t = r_host[i];
++ r_host[i] = r_host[j];
++ r_host[j] = t;
++ }
++}
++
++void reverse_hostname(char *r_host, char const *orig_host) {
++ int len_host = strlen(orig_host);
++
++ /* put reversed hostname into working copy */
++ for (int i = 0; i < len_host; i++)
++ r_host[i] = orig_host[len_host - 1 - i];
++ r_host[len_host] = '\0';
++
++ /* reverse labels (except last) */
++ int p = 0;
++ for (int i = 0; i < len_host; i++)
++ if (r_host[i] == '.') {
++ reverse_string(&r_host[p], i - p);
++ p = i + 1;
++ }
++
++ /* reverse last label */
++ reverse_string(&r_host[p], len_host - p);
++}
++
+ static int gr_format_values(char *ret, size_t ret_len, int ds_num,
+ const data_set_t *ds, const value_list_t *vl,
+ gauge_t const *rates) {
+ size_t offset = 0;
+ int status;
+
+ assert(0 == strcmp(ds->type, vl->type));
+
+ memset(ret, 0, ret_len);
+
+ #define BUFFER_ADD(...) \
+ do { \
+ status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \
+ if (status < 1) { \
+ return -1; \
+ } else if (((size_t)status) >= (ret_len - offset)) { \
+ return -1; \
+ } else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+ BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge);
+ else if (rates != NULL)
+ BUFFER_ADD("%f", rates[ds_num]);
+ else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+ BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
+ else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+ BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
+ else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+ BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
+ else {
+ P_ERROR("gr_format_values: Unknown data source type: %i",
+ ds->ds[ds_num].type);
+ return -1;
+ }
+
+ #undef BUFFER_ADD
+
+ return 0;
+ }
+
+ static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
+ char escape_char, bool preserve_separator) {
+ memset(dst, 0, dst_len);
+
+ if (src == NULL)
+ return;
+
+ for (size_t i = 0; i < dst_len; i++) {
+ if (src[i] == 0) {
+ dst[i] = 0;
+ break;
+ }
+
+ if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) ||
+ iscntrl((int)src[i]))
+ dst[i] = escape_char;
+ else
+ dst[i] = src[i];
+ }
+ }
+
+ static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
+ char const *ds_name, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ char n_host[DATA_MAX_NAME_LEN];
+ char n_plugin[DATA_MAX_NAME_LEN];
+ char n_plugin_instance[DATA_MAX_NAME_LEN];
+ char n_type[DATA_MAX_NAME_LEN];
+ char n_type_instance[DATA_MAX_NAME_LEN];
+
+ char tmp_plugin[DATA_MAX_NAME_LEN + 8];
+ char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
+ char tmp_type[DATA_MAX_NAME_LEN + 6];
+ char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
+ char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
+ char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
+
+ if (prefix == NULL)
+ prefix = "";
+
+ if (postfix == NULL)
+ postfix = "";
+
- gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
- preserve_separator);
++ if (flags & GRAPHITE_REVERSE_HOST) {
++ int len_host = strlen(vl->host);
++ char r_host[len_host + 1];
++ reverse_hostname(r_host, vl->host);
++ gr_copy_escape_part(n_host, r_host, sizeof(n_host), escape_char, 1);
++ } else {
++ gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
++ }
+ gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
+ gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+ sizeof(n_plugin_instance), escape_char, 1);
+ gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
+ gr_copy_escape_part(n_type_instance, vl->type_instance,
+ sizeof(n_type_instance), escape_char, 1);
+
+ snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
+
+ if (n_plugin_instance[0] != '\0')
+ snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
+ ";plugin_instance=%s", n_plugin_instance);
+ else
+ tmp_plugin_instance[0] = '\0';
+
+ if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
+ snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
+ else
+ tmp_type[0] = '\0';
+
+ if (n_type_instance[0] != '\0') {
+ if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
+ strcmp(n_plugin_instance, n_type_instance) != 0)
+ snprintf(tmp_type_instance, sizeof(tmp_type_instance),
+ ";type_instance=%s", n_type_instance);
+ else
+ tmp_type_instance[0] = '\0';
+ } else
+ tmp_type_instance[0] = '\0';
+
+ /* Assert always_append_ds -> ds_name */
+ assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+ if (ds_name != NULL) {
+ snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
+
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
+ else
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
+ ds_name);
+ } else {
+ tmp_ds_name[0] = '\0';
+
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
+ else
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
+ }
+
+ snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
+ postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
+ tmp_type_instance, tmp_ds_name);
+
+ return 0;
+ }
+
+ static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
+ char const *ds_name, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ char n_host[DATA_MAX_NAME_LEN];
+ char n_plugin[DATA_MAX_NAME_LEN];
+ char n_plugin_instance[DATA_MAX_NAME_LEN];
+ char n_type[DATA_MAX_NAME_LEN];
+ char n_type_instance[DATA_MAX_NAME_LEN];
+
+ char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
+ char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
+
+ if (prefix == NULL)
+ prefix = "";
+
+ if (postfix == NULL)
+ postfix = "";
+
+ bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR);
+
++ if (flags & GRAPHITE_REVERSE_HOST) {
++ int len_host = strlen(vl->host);
++ char r_host[len_host + 1];
++ reverse_hostname(r_host, vl->host);
++ gr_copy_escape_part(n_host, r_host, sizeof(n_host), escape_char,
++ preserve_separator);
++ } else {
++ gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
++ preserve_separator);
++ }
+ gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+ sizeof(n_plugin_instance), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_type_instance, vl->type_instance,
+ sizeof(n_type_instance), escape_char, preserve_separator);
+
+ if (n_plugin_instance[0] != '\0')
+ snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin,
+ (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+ n_plugin_instance);
+ else
+ sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin));
+
+ if (n_type_instance[0] != '\0') {
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type));
+ else
+ snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
+ (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+ n_type_instance);
+ } else
+ sstrncpy(tmp_type, n_type, sizeof(tmp_type));
+
+ /* Assert always_append_ds -> ds_name */
+ assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+ if (ds_name != NULL) {
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) &&
+ strcmp(tmp_plugin, tmp_type) == 0)
+ snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix,
+ tmp_plugin, ds_name);
+ else
+ snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
+ tmp_plugin, tmp_type, ds_name);
+ } else
+ snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin,
+ tmp_type);
+
+ return 0;
+ }
+
+ static void escape_graphite_string(char *buffer, char escape_char) {
+ assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
+
+ for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0';
+ head += strcspn(head, GRAPHITE_FORBIDDEN))
+ *head = escape_char;
+ }
+
+ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
+ value_list_t const *vl, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ int status = 0;
+ int buffer_pos = 0;
+
+ gauge_t *rates = NULL;
+ if (flags & GRAPHITE_STORE_RATES) {
+ rates = uc_get_rate(ds, vl);
+ if (rates == NULL) {
+ P_ERROR("format_graphite: error with uc_get_rate");
+ return -1;
+ }
+ }
+
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char const *ds_name = NULL;
+ char key[10 * DATA_MAX_NAME_LEN];
+ char values[512];
+ size_t message_len;
+ char message[1024];
+
+ if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1))
+ ds_name = ds->ds[i].name;
+
+ /* Copy the identifier to `key' and escape it. */
+ if (flags & GRAPHITE_USE_TAGS) {
+ status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
+ postfix, escape_char, flags);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_name_tagged");
+ sfree(rates);
+ return status;
+ }
+ } else {
+ status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
+ escape_char, flags);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_name");
+ sfree(rates);
+ return status;
+ }
+ }
+
+ escape_graphite_string(key, escape_char);
+
+ /* Convert the values to an ASCII representation and put that into
+ * `values'. */
+ status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_values");
+ sfree(rates);
+ return status;
+ }
+
+ /* Compute the graphite command */
+ message_len =
+ (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
+ (unsigned int)CDTIME_T_TO_TIME_T(vl->time));
+ if (message_len >= sizeof(message)) {
+ P_ERROR("format_graphite: message buffer too small: "
+ "Need %" PRIsz " bytes.",
+ message_len + 1);
+ sfree(rates);
+ return -ENOMEM;
+ }
+
+ /* Append it in case we got multiple data set */
+ if ((buffer_pos + message_len) >= buffer_size) {
+ P_ERROR("format_graphite: target buffer too small");
+ sfree(rates);
+ return -ENOMEM;
+ }
+ memcpy((void *)(buffer + buffer_pos), message, message_len);
+ buffer_pos += message_len;
+ buffer[buffer_pos] = '\0';
+ }
+ sfree(rates);
+ return status;
+ } /* int format_graphite */
--- /dev/null
+ /**
+ * collectd - src/utils_format_graphite.h
+ * Copyright (C) 2012 Thomas Meson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Thomas Meson <zllak at hycik.org>
+ **/
+
+ #ifndef UTILS_FORMAT_GRAPHITE_H
+ #define UTILS_FORMAT_GRAPHITE_H 1
+
+ #include "collectd.h"
+
+ #include "plugin.h"
+
+ #define GRAPHITE_STORE_RATES 0x01
+ #define GRAPHITE_SEPARATE_INSTANCES 0x02
+ #define GRAPHITE_ALWAYS_APPEND_DS 0x04
+ #define GRAPHITE_DROP_DUPE_FIELDS 0x08
+ #define GRAPHITE_PRESERVE_SEPARATOR 0x10
+ #define GRAPHITE_USE_TAGS 0x20
++#define GRAPHITE_REVERSE_HOST 0x40
+
+ int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
+ const value_list_t *vl, const char *prefix,
+ const char *postfix, const char escape_char,
+ unsigned int flags);
+
+ #endif /* UTILS_FORMAT_GRAPHITE_H */
* Based on the write_http plugin.
**/
-/* write_graphite plugin configuation example
+/* write_graphite plugin configuration example
*
* <Plugin write_graphite>
* <Carbon>
* LogSendErrors true
* Prefix "collectd"
* UseTags true
+ * ReverseHost false
* </Carbon>
* </Plugin>
*/
#include "collectd.h"
- #include "common.h"
#include "plugin.h"
+ #include "utils/common/common.h"
+ #include "utils/format_graphite/format_graphite.h"
#include "utils_complain.h"
- #include "utils_format_graphite.h"
#include <netdb.h>
cf_util_get_flag(child, &cb->format_flags, GRAPHITE_DROP_DUPE_FIELDS);
else if (strcasecmp("UseTags", child->key) == 0)
cf_util_get_flag(child, &cb->format_flags, GRAPHITE_USE_TAGS);
+ else if (strcasecmp("ReverseHost", child->key) == 0)
+ cf_util_get_flag(child, &cb->format_flags, GRAPHITE_REVERSE_HOST);
else if (strcasecmp("EscapeCharacter", child->key) == 0)
config_set_char(&cb->escape_char, child);
else {