]> mj.ucw.cz Git - libucw.git/commitdiff
Logging: A rough sketch of the logging configuration.
authorMartin Mares <mj@ucw.cz>
Sun, 15 Feb 2009 21:59:49 +0000 (22:59 +0100)
committerMartin Mares <mj@ucw.cz>
Sun, 15 Feb 2009 21:59:49 +0000 (22:59 +0100)
cf/libucw
ucw/Makefile
ucw/log-conf.c [new file with mode: 0644]
ucw/log.h

index 38d2ea5101d8051f05b88dc0138a0a3678a3201b..d525f558b67fd4f9bd763735eb76ef044c54fe92 100644 (file)
--- a/cf/libucw
+++ b/cf/libucw
@@ -172,3 +172,31 @@ MaxRepeatLength            4
 MaxOccurences          4
 
 }
+
+######## Logging ################################################################
+
+Logging {
+
+Stream {
+       Name            logfile
+       FileName        log/test
+}
+
+Stream {
+       Name            logfile2
+       FileName        log/test2
+}
+
+Stream {
+       Name            syslog
+       SyslogFacility  user
+}
+
+Stream {
+       Name            combined
+       Substream       logfile
+       Substream       logfile2
+       Substream       syslog
+}
+
+}
index ed5d249c61b140b962b750ccf9f3b2604c6c1da3..ccfc20ce972dc505b94e99f1d0376ae672a63e92 100644 (file)
@@ -12,7 +12,7 @@ LIBUCW_MODS= \
        alloc alloc_str realloc bigalloc mempool mempool-str mempool-fmt eltpool \
        mmap partmap hashfunc \
        slists simple-lists bitsig \
-       log log-stream log-file log-syslog proctitle \
+       log log-stream log-file log-syslog log-conf proctitle \
        conf-alloc conf-dump conf-input conf-intr conf-journal conf-parse conf-section \
        ipaccess \
        profile \
diff --git a/ucw/log-conf.c b/ucw/log-conf.c
new file mode 100644 (file)
index 0000000..9d36d8a
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ *     UCW Library -- Logging: Configuration of Log Streams
+ *
+ *     (c) 2009 Martin Mares <mj@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
+ */
+
+#include "ucw/lib.h"
+#include "ucw/log.h"
+#include "ucw/conf.h"
+#include "ucw/simple-lists.h"
+
+#include <string.h>
+#include <syslog.h>    // FIXME
+
+struct stream_config {
+  cnode n;
+  char *name;
+  char *file_name;
+  char *syslog_facility;
+  clist substreams;                    // simple_list of names
+  struct log_stream *ls;
+  int mark;                            // Used temporarily in log_config_commit()
+};
+
+static char *
+stream_commit(void *ptr)
+{
+  struct stream_config *c = ptr;
+
+  if (c->file_name && c->syslog_facility)
+    return "Both FileName and SyslogFacility selected";
+  return NULL;
+}
+
+static struct cf_section stream_config = {
+  CF_TYPE(struct stream_config),
+  CF_COMMIT(stream_commit),
+  CF_ITEMS {
+#define P(x) PTR_TO(struct stream_config, x)
+    CF_STRING("Name", P(name)),
+    CF_STRING("FileName", P(file_name)),
+    CF_STRING("SyslogFacility", P(syslog_facility)),
+    CF_LIST("Substream", P(substreams), &cf_string_list_config),
+#undef P
+    CF_END
+  }
+};
+
+static clist log_stream_confs;
+
+static struct stream_config *
+stream_find(const char *name)
+{
+  CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
+    if (!strcmp(c->name, name))
+      return c;
+  return NULL;
+}
+
+static char *
+stream_resolve(struct stream_config *c)
+{
+  if (c->mark == 2)
+    return NULL;
+  if (c->mark == 1)
+    return cf_printf("Log stream `%s' has substreams which refer to itself", c->name);
+
+  c->mark = 1;
+  char *err;
+  CLIST_FOR_EACH(simp_node *, s, c->substreams)
+    {
+      struct stream_config *d = stream_find(s->s);
+      if (!d)
+       return cf_printf("Log stream `%s' refers to unknown substream `%s'", c->name, s->s);
+      if (err = stream_resolve(d))
+       return err;
+    }
+  c->mark = 2;
+  return NULL;
+}
+
+static char *
+log_config_commit(void *ptr UNUSED)
+{
+  // Verify uniqueness of names
+  CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
+    if (stream_find(c->name) != c)
+      return cf_printf("Log stream `%s' defined twice", c->name);
+
+  // Check that all substreams resolve and that there are no cycles
+  char *err;
+  CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
+    if (err = stream_resolve(c))
+      return err;
+
+  return NULL;
+}
+
+static struct cf_section log_config = {
+  CF_COMMIT(log_config_commit),
+  CF_ITEMS {
+    CF_LIST("Stream", &log_stream_confs, &stream_config),
+    CF_END
+  }
+};
+
+static void CONSTRUCTOR
+log_config_init(void)
+{
+  cf_declare_section("Logging", &log_config, 0);
+}
+
+char *
+log_check_configured(const char *name)
+{
+  if (stream_find(name))
+    return NULL;
+  else
+    return cf_printf("Log stream `%s' not found", name);
+}
+
+static struct log_stream *
+do_new_configured(struct stream_config *c)
+{
+  struct log_stream *ls;
+  ASSERT(c);
+
+  if (c->ls)
+    return c->ls;
+
+  if (c->file_name)
+    ls = log_new_file(c->file_name);
+  else if (c->syslog_facility)
+    ls = log_new_syslog(LOG_USER, NULL);       // FIXME: Facility
+  else
+    ls = log_new_stream(sizeof(*ls));
+
+  CLIST_FOR_EACH(simp_node *, s, c->substreams)
+    log_add_substream(ls, do_new_configured(stream_find(s->s)));
+
+  c->ls = ls;
+  return ls;
+}
+
+struct log_stream *
+log_new_configured(const char *name)
+{
+  struct stream_config *c = stream_find(name);
+  if (!c)
+    die("Unable to find log stream %s", name);
+  if (c->ls)
+    return log_ref_stream(c->ls);
+  return do_new_configured(c);
+}
+
+#ifdef TEST
+
+#include "ucw/getopt.h"
+
+int main(int argc, char **argv)
+{
+  log_init(argv[0]);
+  int c;
+  while ((c = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
+    die("No options here.");
+
+  struct log_stream *ls = log_new_configured("combined");
+  msg(L_INFO | ls->regnum, "Hello, universe!");
+
+  return 0;
+}
+
+#endif
index f7baf629a33d146c3679787ca89a0bcec35118e0..bb53dad01936757b5415622820331043de5b5428 100644 (file)
--- a/ucw/log.h
+++ b/ucw/log.h
@@ -203,4 +203,24 @@ int log_switch(void);                      /** Switch log files manually. **/
  **/
 struct log_stream *log_new_syslog(int facility, const char *name);
 
+/***
+ * === Configuring log streams
+ *
+ * If you use the LibUCW mechanism for parsing config files, you can let your
+ * user configure arbitrary log streams in the Logging section of the config file
+ * (see examples in the default config file). LibUCW automatically verifies that
+ * the configuration is consistent (this is performed in the commit hook of the
+ * config section), but it opens the streams only upon request. The following
+ * functions can be used to control that.
+ ***/
+
+/** Open a log stream configured under the specified name and increase its use count. **/
+struct log_stream *log_new_configured(const char *name);
+
+/**
+ * Verify that a stream called @name was configured. If it wasn't, return an error
+ * message. This is intended to be used in configuration commit hooks.
+ **/
+char *log_check_configured(const char *name);
+
 #endif