From: Florian Forster Date: Mon, 12 Sep 2016 13:56:28 +0000 (+0200) Subject: Merge remote-tracking branch 'github/pr/1927' X-Git-Tag: collectd-5.7.0~101 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=59919c47562a2266a6dbd306a78113cb8ddd5ffe;hp=cfa9652f77aced1a4f5e5b611177f84bdd11f609;p=collectd.git Merge remote-tracking branch 'github/pr/1927' --- diff --git a/src/Makefile.am b/src/Makefile.am index b76e43cd..3477dc24 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1285,6 +1285,7 @@ pkglib_LTLIBRARIES += write_log.la write_log_la_SOURCES = write_log.c \ utils_format_graphite.c utils_format_graphite.h write_log_la_LDFLAGS = $(PLUGIN_LDFLAGS) +write_log_la_LIBADD = libformat_json.la endif if BUILD_PLUGIN_WRITE_MONGODB diff --git a/src/battery.c b/src/battery.c index 3e6d7bda..d227d559 100644 --- a/src/battery.c +++ b/src/battery.c @@ -392,47 +392,17 @@ static int sysfs_file_to_buffer(char const *dir, /* {{{ */ char const *basename, char *buffer, size_t buffer_size) { - int status; - FILE *fp; char filename[PATH_MAX]; + int status; ssnprintf (filename, sizeof (filename), "%s/%s/%s", dir, power_supply, basename); - /* No file isn't the end of the world -- not every system will be - * reporting the same set of statistics */ - if (access (filename, R_OK) != 0) - return ENOENT; - - fp = fopen (filename, "r"); - if (fp == NULL) - { - status = errno; - if (status != ENOENT) - { - char errbuf[1024]; - WARNING ("battery plugin: fopen (%s) failed: %s", filename, - sstrerror (status, errbuf, sizeof (errbuf))); - } - return status; - } - - if (fgets (buffer, buffer_size, fp) == NULL) - { - status = errno; - if (status != ENODEV) - { - char errbuf[1024]; - WARNING ("battery plugin: fgets (%s) failed: %s", filename, - sstrerror (status, errbuf, sizeof (errbuf))); - } - fclose (fp); + status = (int) read_file_contents (filename, buffer, buffer_size); + if (status < 0) return status; - } strstripnewline (buffer); - - fclose (fp); return 0; } /* }}} int sysfs_file_to_buffer */ diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 0fa74d54..ab809159 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -7820,6 +7820,26 @@ more than one DS. =back +=head2 Plugin C + +The C plugin writes metrics as INFO log messages. + +This plugin supports two output formats: I and I. + +Synopsis: + + + Format Graphite + + +=over 4 + +=item B I + +The output format to use. Can be one of C or C. + +=back + =head2 Plugin C The C plugin writes data to I, a scalable open-source @@ -9381,6 +9401,10 @@ Available options: =item B I I +=item B I I I + +=item B I I + Match the appropriate field with the given regular expression I. If the regular expression matches, that part that matches is replaced with I. If multiple places of the input buffer match a given regular diff --git a/src/onewire.c b/src/onewire.c index 235eff80..8bb2d816 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -359,6 +359,7 @@ static int cow_read_values (const char *path, const char *name, char *buffer; size_t buffer_size; int status; + char errbuf[1024]; char file[4096]; char *endptr; @@ -373,8 +374,8 @@ static int cow_read_values (const char *path, const char *name, status = OW_get (file, &buffer, &buffer_size); if (status < 0) { - ERROR ("onewire plugin: OW_get (%s/%s) failed. status = %#x;", - path, family_info->features[i].filename, status); + ERROR ("onewire plugin: OW_get (%s/%s) failed. error = %s;", + path, family_info->features[i].filename, sstrerror(errno, errbuf, sizeof (errbuf))); return (-1); } DEBUG ("Read onewire device %s as %s", file, buffer); @@ -430,6 +431,7 @@ static int cow_read_bus (const char *path) char *buffer; size_t buffer_size; int status; + char errbuf[1024]; char *buffer_ptr; char *dummy; @@ -439,8 +441,8 @@ static int cow_read_bus (const char *path) status = OW_get (path, &buffer, &buffer_size); if (status < 0) { - ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;", - path, status); + ERROR ("onewire plugin: OW_get (%s) failed. error = %s;", + path, sstrerror(errno, errbuf, sizeof (errbuf))); return (-1); } DEBUG ("onewire plugin: OW_get (%s) returned: %s", @@ -498,6 +500,7 @@ static int cow_simple_read (void) char *buffer; size_t buffer_size; int status; + char errbuf[1024]; char *endptr; direct_access_element_t *traverse; @@ -514,9 +517,9 @@ static int cow_simple_read (void) status = OW_get (traverse->path, &buffer, &buffer_size); if (status < 0) { - ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;", + ERROR ("onewire plugin: OW_get (%s) failed. status = %s;", traverse->path, - status); + sstrerror(errno, errbuf, sizeof (errbuf))); return (-1); } DEBUG ("onewire plugin: Read onewire device %s as %s", traverse->path, buffer); @@ -590,6 +593,7 @@ static int cow_shutdown (void) static int cow_init (void) { int status; + char errbuf[1024]; if (device_g == NULL) { @@ -601,7 +605,7 @@ static int cow_init (void) status = (int) OW_init (device_g); if (status != 0) { - ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status); + ERROR ("onewire plugin: OW_init(%s) failed: %s.", device_g, sstrerror(errno, errbuf, sizeof (errbuf))); return (1); } diff --git a/src/target_replace.c b/src/target_replace.c index 40a6fec9..dba3a8cf 100644 --- a/src/target_replace.c +++ b/src/target_replace.c @@ -38,11 +38,22 @@ struct tr_action_s { regex_t re; char *replacement; - int may_be_empty; + _Bool may_be_empty; tr_action_t *next; }; +struct tr_meta_data_action_s; +typedef struct tr_meta_data_action_s tr_meta_data_action_t; +struct tr_meta_data_action_s +{ + char *key; + regex_t re; + char *replacement; + + tr_meta_data_action_t *next; +}; + struct tr_data_s { tr_action_t *host; @@ -50,6 +61,7 @@ struct tr_data_s tr_action_t *plugin_instance; /* tr_action_t *type; */ tr_action_t *type_instance; + tr_meta_data_action_t *meta; }; typedef struct tr_data_s tr_data_t; @@ -85,8 +97,23 @@ static void tr_action_destroy (tr_action_t *act) /* {{{ */ sfree (act); } /* }}} void tr_action_destroy */ +static void tr_meta_data_action_destroy (tr_meta_data_action_t *act) /* {{{ */ +{ + if (act == NULL) + return; + + sfree (act->key); + regfree (&act->re); + sfree (act->replacement); + + if (act->next != NULL) + tr_meta_data_action_destroy (act->next); + + sfree (act); +} /* }}} void tr_meta_data_action_destroy */ + static int tr_config_add_action (tr_action_t **dest, /* {{{ */ - const oconfig_item_t *ci, int may_be_empty) + const oconfig_item_t *ci, _Bool may_be_empty) { tr_action_t *act; int status; @@ -131,8 +158,7 @@ static int tr_config_add_action (tr_action_t **dest, /* {{{ */ if (act->replacement == NULL) { ERROR ("tr_config_add_action: tr_strdup failed."); - regfree (&act->re); - sfree (act); + tr_action_destroy (act); return (-ENOMEM); } @@ -153,8 +179,108 @@ static int tr_config_add_action (tr_action_t **dest, /* {{{ */ return (0); } /* }}} int tr_config_add_action */ +static int tr_config_add_meta_action (tr_meta_data_action_t **dest, /* {{{ */ + const oconfig_item_t *ci, _Bool should_delete) +{ + tr_meta_data_action_t *act; + int status; + + if (dest == NULL) + return (-EINVAL); + + if (should_delete) + { + if ((ci->values_num != 2) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || (ci->values[1].type != OCONFIG_TYPE_STRING)) + { + ERROR ("Target `replace': The `%s' option requires exactly two string " + "arguments.", ci->key); + return (-1); + } + } + else + { + if ((ci->values_num != 3) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || (ci->values[1].type != OCONFIG_TYPE_STRING) + || (ci->values[2].type != OCONFIG_TYPE_STRING)) + { + ERROR ("Target `replace': The `%s' option requires exactly three string " + "arguments.", ci->key); + return (-1); + } + } + + if (strlen (ci->values[0].value.string) == 0) + { + ERROR ("Target `replace': The `%s' option does not accept empty string as " + "first argument.", ci->key); + return (-1); + } + + act = calloc (1, sizeof (*act)); + if (act == NULL) + { + ERROR ("tr_config_add_meta_action: calloc failed."); + return (-ENOMEM); + } + + act->key = NULL; + act->replacement = NULL; + + status = regcomp (&act->re, ci->values[1].value.string, REG_EXTENDED); + if (status != 0) + { + char errbuf[1024] = ""; + + /* regerror assures null termination. */ + regerror (status, &act->re, errbuf, sizeof (errbuf)); + ERROR ("Target `replace': Compiling the regular expression `%s' " + "failed: %s.", + ci->values[1].value.string, errbuf); + sfree (act->key); + sfree (act); + return (-EINVAL); + } + + act->key = tr_strdup (ci->values[0].value.string); + if (act->key == NULL) + { + ERROR ("tr_config_add_meta_action: tr_strdup failed."); + tr_meta_data_action_destroy (act); + return (-ENOMEM); + } + + if (!should_delete) { + act->replacement = tr_strdup (ci->values[2].value.string); + if (act->replacement == NULL) + { + ERROR ("tr_config_add_meta_action: tr_strdup failed."); + tr_meta_data_action_destroy (act); + return (-ENOMEM); + } + } + + /* Insert action at end of list. */ + if (*dest == NULL) + *dest = act; + else + { + tr_meta_data_action_t *prev; + + prev = *dest; + while (prev->next != NULL) + prev = prev->next; + + prev->next = act; + } + + return (0); +} /* }}} int tr_config_add_meta_action */ + static int tr_action_invoke (tr_action_t *act_head, /* {{{ */ - char *buffer_in, size_t buffer_in_size, int may_be_empty) + char *buffer_in, size_t buffer_in_size, _Bool may_be_empty) { int status; char buffer[DATA_MAX_NAME_LEN]; @@ -215,6 +341,119 @@ static int tr_action_invoke (tr_action_t *act_head, /* {{{ */ return (0); } /* }}} int tr_action_invoke */ +static int tr_meta_data_action_invoke ( /* {{{ */ + tr_meta_data_action_t *act_head, meta_data_t **dest) +{ + int status; + regmatch_t matches[8] = { [0] = { 0 } }; + + if (act_head == NULL) + return (-EINVAL); + + if ((*dest) == NULL) /* nothing to do */ + return (0); + + for (tr_meta_data_action_t *act = act_head; act != NULL; act = act->next) + { + char temp[DATA_MAX_NAME_LEN]; + char *subst_status; + int value_type; + int meta_data_status; + char *value; + meta_data_t *result; + + value_type = meta_data_type (*dest, act->key); + if (value_type == 0) /* not found */ + continue; + if (value_type != MD_TYPE_STRING) + { + WARNING ("Target `replace': Attempting replace on metadata key `%s', " + "which isn't a string.", + act->key); + continue; + } + + meta_data_status = meta_data_get_string (*dest, act->key, &value); + if (meta_data_status != 0) + { + ERROR ("Target `replace': Unable to retrieve metadata value for `%s'.", + act->key); + return (meta_data_status); + } + + DEBUG ("target_replace plugin: tr_meta_data_action_invoke: `%s' " + "old value = `%s'", act->key, value); + + status = regexec (&act->re, value, + STATIC_ARRAY_SIZE (matches), matches, + /* flags = */ 0); + if (status == REG_NOMATCH) + { + sfree (value); + continue; + } + else if (status != 0) + { + char errbuf[1024] = ""; + + regerror (status, &act->re, errbuf, sizeof (errbuf)); + ERROR ("Target `replace': Executing a regular expression failed: %s.", + errbuf); + sfree (value); + continue; + } + + if (act->replacement == NULL) + { + /* no replacement; delete the key */ + DEBUG ("target_replace plugin: tr_meta_data_action_invoke: " + "deleting `%s'", act->key); + meta_data_delete (*dest, act->key); + sfree (value); + continue; + } + + subst_status = subst (temp, sizeof (temp), value, + (size_t) matches[0].rm_so, (size_t) matches[0].rm_eo, act->replacement); + if (subst_status == NULL) + { + ERROR ("Target `replace': subst (value = %s, start = %zu, end = %zu, " + "replacement = %s) failed.", + value, (size_t) matches[0].rm_so, (size_t) matches[0].rm_eo, + act->replacement); + sfree (value); + continue; + } + + DEBUG ("target_replace plugin: tr_meta_data_action_invoke: `%s' " + "value `%s' -> `%s'", act->key, value, temp); + + if ((result = meta_data_create()) == NULL) + { + ERROR ("Target `replace': failed to create metadata for `%s'.", + act->key); + sfree (value); + return (-ENOMEM); + } + + meta_data_status = meta_data_add_string (result, act->key, temp); + if (meta_data_status != 0) + { + ERROR ("Target `replace': Unable to set metadata value for `%s'.", + act->key); + meta_data_destroy (result); + sfree (value); + return (meta_data_status); + } + + meta_data_clone_merge (dest, result); + meta_data_destroy (result); + sfree (value); + } /* for (act = act_head; act != NULL; act = act->next) */ + + return (0); +} /* }}} int tr_meta_data_action_invoke */ + static int tr_destroy (void **user_data) /* {{{ */ { tr_data_t *data; @@ -231,6 +470,7 @@ static int tr_destroy (void **user_data) /* {{{ */ tr_action_destroy (data->plugin_instance); /* tr_action_destroy (data->type); */ tr_action_destroy (data->type_instance); + tr_meta_data_action_destroy (data->meta); sfree (data); return (0); @@ -253,6 +493,7 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ data->plugin_instance = NULL; /* data->type = NULL; */ data->type_instance = NULL; + data->meta = NULL; status = 0; for (int i = 0; i < ci->children_num; i++) @@ -277,6 +518,12 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ else if (strcasecmp ("TypeInstance", child->key) == 0) status = tr_config_add_action (&data->type_instance, child, /* may be empty = */ 1); + else if (strcasecmp ("MetaData", child->key) == 0) + status = tr_config_add_meta_action (&data->meta, child, + /* should delete = */ 0); + else if (strcasecmp ("DeleteMetaData", child->key) == 0) + status = tr_config_add_meta_action (&data->meta, child, + /* should delete = */ 1); else { ERROR ("Target `replace': The `%s' configuration option is not understood " @@ -295,7 +542,8 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ && (data->plugin == NULL) && (data->plugin_instance == NULL) /* && (data->type == NULL) */ - && (data->type_instance == NULL)) + && (data->type_instance == NULL) + && (data->meta == NULL)) { ERROR ("Target `replace': You need to set at least one of `Host', " "`Plugin', `PluginInstance' or `TypeInstance'."); @@ -330,13 +578,18 @@ static int tr_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */ return (-EINVAL); } + if (data->meta != NULL) + { + tr_meta_data_action_invoke (data->meta, &(vl->meta)); + } + #define HANDLE_FIELD(f,e) \ if (data->f != NULL) \ tr_action_invoke (data->f, vl->f, sizeof (vl->f), e) HANDLE_FIELD (host, 0); HANDLE_FIELD (plugin, 0); HANDLE_FIELD (plugin_instance, 1); - /* HANDLE_FIELD (type); */ + /* HANDLE_FIELD (type, 0); */ HANDLE_FIELD (type_instance, 1); return (FC_TARGET_CONTINUE); diff --git a/src/write_log.c b/src/write_log.c index b0dc6f11..e10a912e 100644 --- a/src/write_log.c +++ b/src/write_log.c @@ -31,20 +31,26 @@ #include "plugin.h" #include "utils_format_graphite.h" +#include "utils_format_json.h" #include -#define WL_BUF_SIZE 8192 +#define WL_BUF_SIZE 16384 -static int wl_write_messages (const data_set_t *ds, const value_list_t *vl) +#define WL_FORMAT_GRAPHITE 1 +#define WL_FORMAT_JSON 2 + +/* Plugin:WriteLog has to also operate without a config, so use a global. */ +int wl_format = WL_FORMAT_GRAPHITE; + +static int wl_write_graphite (const data_set_t *ds, const value_list_t *vl) { char buffer[WL_BUF_SIZE] = { 0 }; int status; if (0 != strcmp (ds->type, vl->type)) { - ERROR ("write_log plugin: DS type does not match " - "value list type"); + ERROR ("write_log plugin: DS type does not match value list type"); return -1; } @@ -56,20 +62,95 @@ static int wl_write_messages (const data_set_t *ds, const value_list_t *vl) INFO ("write_log values:\n%s", buffer); return (0); -} /* int wl_write_messages */ +} /* int wl_write_graphite */ + +static int wl_write_json (const data_set_t *ds, const value_list_t *vl) +{ + char buffer[WL_BUF_SIZE] = { 0 }; + size_t bfree = sizeof(buffer); + size_t bfill = 0; + + if (0 != strcmp (ds->type, vl->type)) + { + ERROR ("write_log plugin: DS type does not match value list type"); + return -1; + } + + format_json_initialize(buffer, &bfill, &bfree); + format_json_value_list(buffer, &bfill, &bfree, ds, vl, + /* store rates = */ 0); + format_json_finalize(buffer, &bfill, &bfree); + + INFO ("write_log values:\n%s", buffer); + + return (0); +} /* int wl_write_json */ static int wl_write (const data_set_t *ds, const value_list_t *vl, __attribute__ ((unused)) user_data_t *user_data) { - int status; + int status = 0; - status = wl_write_messages (ds, vl); + if (wl_format == WL_FORMAT_GRAPHITE) + { + status = wl_write_graphite (ds, vl); + } + else if (wl_format == WL_FORMAT_JSON) + { + status = wl_write_json (ds, vl); + } return (status); } +static int wl_config (oconfig_item_t *ci) /* {{{ */ +{ + _Bool format_seen = 0; + + for (int i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Format", child->key) == 0) + { + char str[16]; + + if (cf_util_get_string_buffer (child, str, sizeof (str)) != 0) + continue; + + if (format_seen) + { + WARNING ("write_log plugin: Redefining option `%s'.", + child->key); + } + format_seen = 1; + + if (strcasecmp ("Graphite", str) == 0) + wl_format = WL_FORMAT_GRAPHITE; + else if (strcasecmp ("JSON", str) == 0) + wl_format = WL_FORMAT_JSON; + else + { + ERROR ("write_log plugin: Unknown format `%s' for option `%s'.", + str, child->key); + return (-EINVAL); + } + } + else + { + ERROR ("write_log plugin: Invalid configuration option: `%s'.", + child->key); + return (-EINVAL); + } + } + + return (0); +} /* }}} int wl_config */ + void module_register (void) { + plugin_register_complex_config ("write_log", wl_config); + /* If config is supplied, the global wl_format will be set. */ plugin_register_write ("write_log", wl_write, NULL); }