Code

src/utils_logtail.[ch]: Add a module to provide facilities for logfile tailing.
authorLuke Heberling <collectd@c-ware.com>
Sat, 23 Feb 2008 11:37:48 +0000 (12:37 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 23 Feb 2008 11:37:48 +0000 (12:37 +0100)
src/Makefile.am
src/utils_logtail.c [new file with mode: 0644]
src/utils_logtail.h [new file with mode: 0644]

index 50ad0c9974a647ccbb8de853ff074a5adc056feb..a0b4c2df700538fa9d90e770649e65791ddf2629 100644 (file)
@@ -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 (file)
index 0000000..e25703d
--- /dev/null
@@ -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 <lukeh at c-ware.com>
+ *
+ * Description:
+ *   Encapsulates useful code to plugins which must parse a log file.
+ */
+
+#include <time.h>
+#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 (file)
index 0000000..156dcb3
--- /dev/null
@@ -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 <lukeh at c-ware.com>
+ *
+ * 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);
+