From 0b3f5bda614cbb0795fdb92d6473251004afc86d Mon Sep 17 00:00:00 2001 From: Luke Heberling Date: Sat, 23 Feb 2008 12:37:48 +0100 Subject: [PATCH] src/utils_logtail.[ch]: Add a module to provide facilities for logfile tailing. --- src/Makefile.am | 1 + src/utils_logtail.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils_logtail.h | 168 +++++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 src/utils_logtail.c create mode 100644 src/utils_logtail.h diff --git a/src/Makefile.am b/src/Makefile.am index 50ad0c99..a0b4c2df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,6 +30,7 @@ collectd_SOURCES = collectd.c collectd.h \ utils_cache.c utils_cache.h \ utils_ignorelist.c utils_ignorelist.h \ utils_llist.c utils_llist.h \ + utils_logtail.c utils_logtail.h \ utils_mount.c utils_mount.h \ utils_tail.c utils_tail.h \ utils_threshold.c utils_threshold.h \ diff --git a/src/utils_logtail.c b/src/utils_logtail.c new file mode 100644 index 00000000..e25703d8 --- /dev/null +++ b/src/utils_logtail.c @@ -0,0 +1,417 @@ +/* + * collectd - src/utils_logtail.c + * Copyright (C) 2007-2008 C-Ware, Inc. + * + * 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: + * Luke Heberling + * + * Description: + * Encapsulates useful code to plugins which must parse a log file. + */ + +#include +#include "plugin.h" +#include "utils_tail.h" +#include "utils_llist.h" +#include "utils_avltree.h" + +#define DESTROY_INSTANCE(inst) \ + do { \ + if (inst != NULL) \ + { \ + if (inst->name != NULL) \ + free (inst->name); \ + if (inst->tail != NULL) \ + cu_tail_destroy (inst->tail); \ + if (inst->tree != NULL) \ + c_avl_destroy (inst->tree); \ + assert (inst->list == NULL || \ + llist_size (inst->list) == 0); \ + if (inst->list != NULL) \ + llist_destroy (inst->list); \ + if (inst->counters != NULL) \ + free (inst->counters); \ + free (inst); \ + } \ + } while (0) + +struct logtail_instance_s +{ + char *name; + cu_tail_t *tail; + c_avl_tree_t *tree; + llist_t *list; + uint cache_size; + unsigned long *counters; +}; +typedef struct logtail_instance_s logtail_instance_t; + +static void submit (const char *plugin, const char *instance, + const char *name, unsigned long value) +{ + value_list_t vl = VALUE_LIST_INIT; + value_t values[1]; + const data_set_t *ds; + + ds = plugin_get_ds (name); + if (ds == NULL) + return; + + if (ds->ds->type == DS_TYPE_GAUGE) + values[0].gauge = (float)value; + else + values[0].counter = value; + + vl.values = values; + vl.values_len = 1; + vl.time = time (NULL); + strncpy (vl.host, hostname_g, sizeof (vl.host)); + strncpy (vl.plugin, plugin, sizeof (vl.plugin)); + strncpy (vl.type_instance, "", sizeof (vl.type_instance)); + strncpy (vl.plugin_instance, instance, sizeof (vl.plugin_instance)); + + plugin_dispatch_values (name, &vl); +} /* static void submit */ + +int logtail_term (llist_t **instances) +{ + llentry_t *entry; + llentry_t *prev; + + llentry_t *lentry; + llentry_t *lprev; + + logtail_instance_t *instance; + + if (*instances != NULL) + { + entry = llist_head (*instances); + while (entry) + { + prev = entry; + entry = entry->next; + + instance = prev->value; + if (instance->list != NULL) + { + lentry = llist_head (instance->list); + while (lentry) + { + lprev = lentry; + lentry = lentry->next; + if (lprev->key != NULL) + free (lprev->key); + if (lprev->value != NULL) + free (lprev->value); + llist_remove (instance->list, lprev); + llentry_destroy (lprev); + } + } + + llist_remove (*instances, prev); + llentry_destroy (prev); + DESTROY_INSTANCE (instance); + } + + llist_destroy (*instances); + *instances = NULL; + } + + return (0); +} /* int logtail_term */ + +int logtail_init (llist_t **instances) +{ + if (*instances == NULL) + *instances = llist_create(); + + return (*instances == NULL); +} /* int logtail_init */ + +int logtail_read (llist_t **instances, tailfunc *func, char *plugin, char **names) +{ + llentry_t *entry; + logtail_instance_t *instance; + char buffer[2048]; + char **name; + unsigned long *counter; + + for (entry = llist_head (*instances); entry != NULL; entry = entry->next ) + { + instance = (logtail_instance_t *)entry->value; + cu_tail_read (instance->tail, buffer, + sizeof (buffer), func, instance); + + for (name = names, counter = instance->counters; + *name != NULL; ++name, ++counter) + submit (plugin, instance->name, *name, *counter); + } + + return (0); +} /* int logtail_read */ + +int logtail_config (llist_t **instances, oconfig_item_t *ci, char *plugin, + char **names, char *default_file, int default_cache_size) +{ + int counterslen = 0; + logtail_instance_t *instance; + + llentry_t *entry; + char *tail_file; + + oconfig_item_t *gchild; + int gchildren; + + oconfig_item_t *child = ci->children; + int children = ci->children_num; + + while (*(names++) != NULL) + counterslen += sizeof (unsigned long); + + if (*instances == NULL) + { + *instances = llist_create(); + if (*instances == NULL) + return 1; + } + + + for (; children; --children, ++child) + { + tail_file = NULL; + + if (strcmp (child->key, "Instance") != 0) + { + WARNING ("%s plugin: Ignoring unknown" + " config option `%s'.", plugin, child->key); + continue; + } + + if ((child->values_num != 1) || + (child->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("%s plugin: `Instance' needs exactly" + " one string argument.", plugin); + continue; + } + + instance = malloc (sizeof (logtail_instance_t)); + if (instance == NULL) + { + ERROR ("%s plugin: `malloc' failed.", plugin); + return 1; + } + memset (instance, '\0', sizeof (logtail_instance_t)); + + instance->counters = malloc (counterslen); + if (instance->counters == NULL) + { + ERROR ("%s plugin: `malloc' failed.", plugin); + DESTROY_INSTANCE (instance); + return 1; + } + memset (instance->counters, '\0', counterslen); + + instance->name = strdup (child->values[0].value.string); + if (instance->name == NULL) + { + ERROR ("%s plugin: `strdup' failed.", plugin); + DESTROY_INSTANCE (instance); + return 1; + } + + instance->list = llist_create(); + if (instance->list == NULL) + { + ERROR ("%s plugin: `llist_create' failed.", plugin); + DESTROY_INSTANCE (instance); + return 1; + } + + instance->tree = c_avl_create ((void *)strcmp); + if (instance->tree == NULL) + { + ERROR ("%s plugin: `c_avl_create' failed.", plugin); + DESTROY_INSTANCE (instance); + return 1; + } + + entry = llentry_create (instance->name, instance); + if (entry == NULL) + { + ERROR ("%s plugin: `llentry_create' failed.", plugin); + DESTROY_INSTANCE (instance); + return 1; + } + + gchild = child->children; + gchildren = child->children_num; + + for (; gchildren; --gchildren, ++gchild) + { + if (strcmp (gchild->key, "LogFile") == 0) + { + if (gchild->values_num != 1 || + gchild->values[0].type != OCONFIG_TYPE_STRING) + { + WARNING ("%s plugin: config option `%s'" + " should have exactly one string value.", + plugin, gchild->key); + continue; + } + if (tail_file != NULL) + { + WARNING ("%s plugin: ignoring extraneous" + " `LogFile' config option.", plugin); + continue; + } + tail_file = gchild->values[0].value.string; + } + else if (strcmp (gchild->key, "CacheSize") == 0) + { + if (gchild->values_num != 1 + || gchild->values[0].type != OCONFIG_TYPE_NUMBER) + { + WARNING ("%s plugin: config option `%s'" + " should have exactly one numerical value.", + plugin, gchild->key); + continue; + } + if (instance->cache_size) + { + WARNING ("%s plugin: ignoring extraneous" + " `CacheSize' config option.", plugin); + continue; + } + instance->cache_size = gchild->values[0].value.number; + } + else + { + WARNING ("%s plugin: Ignoring unknown config option" + " `%s'.", plugin, gchild->key); + continue; + } + + if (gchild->children_num) + { + WARNING ("%s plugin: config option `%s' should not" + " have children.", plugin, gchild->key); + } + } + + if (tail_file == NULL) + tail_file = default_file; + instance->tail = cu_tail_create (tail_file); + if (instance->tail == NULL) + { + ERROR ("%s plugin: `cu_tail_create' failed.", plugin); + DESTROY_INSTANCE (instance); + + llentry_destroy (entry); + return 1; + } + + if (instance->cache_size == 0) + instance->cache_size = default_cache_size; + + llist_append (*instances, entry); + } + + return 0; +} /* int logtail_config */ + +unsigned long *logtail_counters (logtail_instance_t *instance) +{ + return instance->counters; +} /* unsigned log *logtail_counters */ + +int logtail_cache (logtail_instance_t *instance, char *plugin, char *key, void **data, int len) +{ + llentry_t *entry = NULL; + + if (c_avl_get (instance->tree, key, (void*)&entry) == 0) + { + *data = entry->value; + return (0); + } + + if ((key = strdup (key)) == NULL) + { + ERROR ("%s plugin: `strdup' failed.", plugin); + return (0); + } + + if (data != NULL && (*data = malloc (len)) == NULL) + { + ERROR ("%s plugin: `malloc' failed.", plugin); + free (key); + return (0); + } + + if (data != NULL) + memset (*data, '\0', len); + + entry = llentry_create (key, data == NULL ? NULL : *data); + if (entry == NULL) + { + ERROR ("%s plugin: `llentry_create' failed.", plugin); + free (key); + if (data !=NULL) + free (*data); + return (0); + } + + if (c_avl_insert (instance->tree, key, entry) != 0) + { + ERROR ("%s plugin: `c_avl_insert' failed.", plugin); + llentry_destroy (entry); + free (key); + if (data != NULL) + free (*data); + return (0); + } + + llist_prepend (instance->list, entry); + + while (llist_size (instance->list) > instance->cache_size && + (entry = llist_tail (instance->list)) != NULL ) + { + c_avl_remove (instance->tree, entry->key, NULL, NULL); + llist_remove (instance->list, entry); + free (entry->key); + if (entry->value != NULL) + free (entry->value); + llentry_destroy (entry); + } + + return (1); +} + +void logtail_decache (logtail_instance_t *instance, char *key) +{ + llentry_t *entry = NULL; + if (c_avl_remove (instance->tree, key, NULL, (void*)&entry)) + return; + + llist_remove (instance->list, entry); + free (entry->key); + if (entry->value != NULL) + free (entry->value); + + llentry_destroy (entry); +} + +/* vim: set sw=2 sts=2 ts=8 : */ diff --git a/src/utils_logtail.h b/src/utils_logtail.h new file mode 100644 index 00000000..156dcb38 --- /dev/null +++ b/src/utils_logtail.h @@ -0,0 +1,168 @@ +/* + * collectd - src/utils_logtail.h + * Copyright (C) 2007-2008 C-Ware, Inc. + * + * 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: + * Luke Heberling + * + * Description: + * Encapsulates useful code to plugins which must parse a log file. + * + */ + +#include "utils_llist.h" +#include "utils_tail.h" + +struct logtail_instance_s; +typedef struct logtail_instance_s logtail_instance_t; + +/* + * NAME + * logtail_term + * + * DESCRIPTION + * Call to release resources associated with the plugin. + * + * PARAMETERS + * `instances' The handle used to identify the plugin. + * + * RETURN VALUE + * Zero on success, nonzero on failure. + */ +int logtail_term (llist_t **instances); + +/* + * NAME + * logtail_term + * + * DESCRIPTION + * Call to initialize the plugin + * + * PARAMETERS + * `instances' The handle used to identify the plugin. + * + * RETURN VALUE + * Zero on success, nonzero on failure. + */ +int logtail_init (llist_t **instances); + +/* + * NAME + * logtail_read + * + * DESCRIPTION + * Looks for more data in the log file, sends each line + * through the given function, and submits the counters to + * collectd. + * + * PARAMETERS + * `instances' The handle used to identify the plugin. + * + * `func' The function used to parse each line from the log. + * + * `plugin' The name of the plugin. + * + * `names' An array of counter names in the same order as the + * counters themselves. + * + * RETURN VALUE + * Zero on success, nonzero on failure. + */ +int logtail_read (llist_t **instances, tailfunc *func, char *plugin, + char **names); + +/* + * NAME + * logtail_config + * + * DESCRIPTION + * Configures the logtail instance for the given plugin. + * + * PARAMETERS + * `instances' The handle used to identify the plugin. + * + * `ci' The configuration item from collectd. + * + * `plugin' The name of the plugin. + * + * `names' An array of counter names in the same order as the + * counters themselves. + * + * `default_file' The default log file if none is found in the + * configuration. + * + * `default_cache_size' The default cache size if none is found in the + * configuration. + * + * RETURN VALUE + * Zero on success, nonzero on failure. + */ +int logtail_config (llist_t **instances, oconfig_item_t *ci, char *plugin, + char **names, char *default_file, + int default_cache_size); + +/* + * NAME + * logtail_counters + * + * DESCRIPTION + * Returns the counters maintained for the plugin. + * + * PARAMETERS + * `instance' The handle used to identify the logtail instance. + * + */ +unsigned long *logtail_counters (logtail_instance_t *instance); + +/* + * NAME + * logtail_cache + * + * DESCRIPTION + * Stores the data in the cache. + * + * PARAMETERS + * `instance' The handle used to identify the logtail instance. + * + * `plugin' The name of the plugin. + * + * `key' The key to identify the cached data. + * + * `data' The data to cache. + * + * `len' The length of the data to cache. + * + * RETURN VALUE + * Zero on success, nonzero on failure. + */ +int logtail_cache (logtail_instance_t *instance, char *plugin, char *key, + void **data, int len); + +/* + * NAME + * logtail_decache + * + * DESCRIPTION + * Removes the data from the cache. + * + * PARAMETERS + * `instance' The handle used to identify the logtail instance. + * + * `key' The key to identify the cached data. + * + */ +void logtail_decache (logtail_instance_t *instance, char *key); + -- 2.11.0