From f5bc3454ff581d0966e8b7384f41d67582264cb3 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 15 Feb 2009 22:59:49 +0100 Subject: [PATCH] Logging: A rough sketch of the logging configuration. --- cf/libucw | 28 ++++++++ ucw/Makefile | 2 +- ucw/log-conf.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++ ucw/log.h | 20 ++++++ 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 ucw/log-conf.c diff --git a/cf/libucw b/cf/libucw index 38d2ea51..d525f558 100644 --- 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 +} + +} diff --git a/ucw/Makefile b/ucw/Makefile index ed5d249c..ccfc20ce 100644 --- a/ucw/Makefile +++ b/ucw/Makefile @@ -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 index 00000000..9d36d8a9 --- /dev/null +++ b/ucw/log-conf.c @@ -0,0 +1,176 @@ +/* + * UCW Library -- Logging: Configuration of Log Streams + * + * (c) 2009 Martin Mares + * + * 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 +#include // 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 diff --git a/ucw/log.h b/ucw/log.h index f7baf629..bb53dad0 100644 --- 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 -- 2.39.2