From 464a5c3067d1fbed8e2f9899e8e3459e7948e07e Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Fri, 13 Feb 2009 17:22:43 +0100 Subject: [PATCH] Logging: The Great Shuffle I have split the new logging machinery to several files and started merging it with the old logger. The intention behind the split is that programs which do not create their own log streams should include only (which brings only the minimum set of symbols) and link only a minimalistic library module, while the programs in need of the full power of the new logger include and link with all log-* modules. I have also started renaming `ls_*' to `log_*', but so far only at a couple of places. (Beware, most parts of the trees are currently left in an uncompilable state. Use `make obj/ucw/log-t' for a working test program.) --- ucw/Makefile | 2 +- ucw/lib.h | 41 +-- ucw/log-file.c | 62 ++++- ucw/log-stream.c | 169 ++++++++++++ ucw/log-syslog.c | 68 +++++ ucw/log.c | 249 +++++++++++++---- ucw/{logstream.h => log.h} | 28 +- ucw/logstream.c | 535 ------------------------------------- 8 files changed, 533 insertions(+), 621 deletions(-) create mode 100644 ucw/log-stream.c create mode 100644 ucw/log-syslog.c rename ucw/{logstream.h => log.h} (92%) delete mode 100644 ucw/logstream.c diff --git a/ucw/Makefile b/ucw/Makefile index 4e477b86..ed5d249c 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-file logstream proctitle \ + log log-stream log-file log-syslog proctitle \ conf-alloc conf-dump conf-input conf-intr conf-journal conf-parse conf-section \ ipaccess \ profile \ diff --git a/ucw/lib.h b/ucw/lib.h index 221adf66..22db7ef1 100644 --- a/ucw/lib.h +++ b/ucw/lib.h @@ -80,40 +80,41 @@ #error This program requires the GNU C compiler. #endif -#include "ucw/logstream.h" -/* Logging */ -/* -#define L_DEBUG 'D' * Debugging messages * -#define L_INFO 'I' * Informational msgs, warnings and errors * -#define L_WARN 'W' -#define L_ERROR 'E' -#define L_INFO_R 'i' * Errors caused by external events * -#define L_WARN_R 'w' -#define L_ERROR_R 'e' -#define L_FATAL '!' * die() * -*/ -#define L_SIGHANDLER 0x10000 /* Avoid operations that are unsafe in signal handlers */ +/* Logging (more in ) */ + +enum log_levels +{ + L_DEBUG=0, // 'D' - Debugging + L_INFO, // 'I' - Informational + L_WARN, // 'W' - Warning + L_ERROR, // 'E' - Error, but non-critical + L_INFO_R, // 'i' - An alternative set of levels for messages caused by remote events + L_WARN_R, // 'w' (e.g., a packet received via network) + L_ERROR_R, // 'e' + L_FATAL, // '!' - Fatal error +}; + +#define L_SIGHANDLER 0x80000000 /* Avoid operations that are unsafe in signal handlers */ extern char *log_title; /* NULL - print no title, default is program name given to log_init() */ -extern char *log_filename; /* Expanded name of the current log file */ extern int log_pid; /* 0 if shouldn't be logged */ -extern int log_precise_timings; /* Include microsecond timestamps in log messages */ -extern void (*log_die_hook)(void); -struct tm; -extern void (*log_switch_hook)(struct tm *tm); +extern void (*log_die_hook)(void); // FIXME -void msg(uns cat, const char *fmt, ...) FORMAT_CHECK(printf,2,3); -void vmsg(uns cat, const char *fmt, va_list args); +void msg(uns flags, const char *fmt, ...) FORMAT_CHECK(printf,2,3); +void vmsg(uns flags, const char *fmt, va_list args); void die(const char *, ...) NONRET FORMAT_CHECK(printf,1,2); + void log_init(const char *argv0); void log_file(const char *name); void log_fork(void); /* Call after fork() to update log_pid */ /* If the log name contains metacharacters for date and time, we switch the logs * automatically whenever the name changes. You can disable it and switch explicitly. */ +#if 0 // FIXME int log_switch(void); void log_switch_disable(void); void log_switch_enable(void); +#endif void assert_failed(const char *assertion, const char *file, int line) NONRET; void assert_failed_noinfo(void) NONRET; diff --git a/ucw/log-file.c b/ucw/log-file.c index ec05396c..a07f705f 100644 --- a/ucw/log-file.c +++ b/ucw/log-file.c @@ -1,13 +1,15 @@ /* - * UCW Library -- Keeping of Log Files + * UCW Library -- Logging to Files * - * (c) 1997--2006 Martin Mares + * (c) 1997--2009 Martin Mares + * (c) 2008 Tomas Gavenciak * * 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/lfs.h" #include "ucw/threads.h" @@ -16,6 +18,9 @@ #include #include #include +#include + +#if 0 // FIXME static char *log_name_patt; static int log_params; @@ -107,6 +112,59 @@ log_switch_enable(void) log_switch_nest--; } +#endif + +/* destructor for standard files */ +static void ls_fdfile_close(struct log_stream *ls) +{ + ASSERT(ls); + close(ls->idata); + if(ls->name) + xfree(ls->name); +} + +/* handler for standard files */ +static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat UNUSED) +{ + int len = strlen(m); + int r = write(ls->idata, m, len); + /* TODO: check the errors here? */ + if (r!=len) + return errno; + return 0; +} + +/* assign log to a file descriptor */ +/* initialize with the default formatting, does NOT close the descriptor */ +struct log_stream *ls_fdfile_new(int fd) +{ + struct log_stream *ls=ls_new(); + ls->idata=fd; + ls->msgfmt=LSFMT_DEFAULT; + ls->handler=ls_fdfile_handler; + return ls; +} + +/* open() a file (append mode) */ +/* initialize with the default formatting */ +struct log_stream *ls_file_new(const char *path) +{ + struct log_stream *ls; + int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (fd<0) + { + ls_msg(L_ERROR, "Opening logfile '%s' failed: %m.", path); + return NULL; + } + ls = ls_new(); + ls->name = xstrdup(path); + ls->idata = fd; + ls->msgfmt = LSFMT_DEFAULT; + ls->handler = ls_fdfile_handler; + ls->close = ls_fdfile_close; + return ls; +} + #ifdef TEST int main(int argc, char **argv) diff --git a/ucw/log-stream.c b/ucw/log-stream.c new file mode 100644 index 00000000..748164c8 --- /dev/null +++ b/ucw/log-stream.c @@ -0,0 +1,169 @@ +/* + * UCW Library -- Logging: Management of Log Streams + * + * (c) 2008 Tomas Gavenciak + * (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/clists.h" +#include "ucw/simple-lists.h" +#include "ucw/log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Flag indicating initialization of the module */ +static int ls_initialized = 0; + +/* The head of the list of freed log_streams indexes in ls_streams.ptr (-1 if none free). + * Freed positions in ls_streams.ptr are connected into a linked list in the following way: + * ls_streams.ptr[ls_streams_free].idata is the index of next freed position (or -1) */ +static int ls_streams_free = -1; + +/* Initialize the logstream module. + * It is not neccessary to call this explicitely as it is called by + * the first ls_new() (for backward compatibility and ease of use). */ +static void ls_init_module(void) +{ + if (ls_initialized) return; + + /* create the grow array */ + lsbuf_init(&log_streams); + lsbuf_set_size(&log_streams, LS_INIT_STREAMS); + + bzero(log_streams.ptr, sizeof(struct log_stream*) * (log_streams.len)); + ls_streams_free = -1; + + ls_initialized = 1; + + /* init the default stream (0) as forwarder to fd2 */ + struct log_stream *ls = ls_new(); + ASSERT(ls == log_streams.ptr[0]); + ASSERT(ls->regnum == 0); + ls->name = "default"; + ls_add_substream(ls, (struct log_stream *) &ls_default_log); + + /* log this */ + ls_msg(L_DEBUG, "logstream module initialized."); +} + +/* close all open streams, un-initialize the module, free all memory, + * use only ls_default_log */ +void ls_close_all(void) +{ + int i; + + if (!ls_initialized) return; + + for (i=0; iregnum>=0) + ls_close(log_streams.ptr[i]); + xfree(log_streams.ptr[i]); + } + + /* set to the default state */ + lsbuf_done(&log_streams); + log_streams_after=0; + ls_streams_free=-1; + ls_initialized = 0; +} + +/* add a new substream, malloc()-ate a new simp_node */ +void ls_add_substream(struct log_stream *where, struct log_stream *what) +{ + ASSERT(where); + ASSERT(what); + + simp_node *n = xmalloc(sizeof(simp_node)); + n->p = what; + clist_add_tail(&(where->substreams), (cnode*)n); +} + +/* remove all occurences of a substream, free() the simp_node */ +/* return number of deleted entries */ +int ls_rm_substream(struct log_stream *where, struct log_stream *what) +{ + void *tmp; + int cnt=0; + ASSERT(where); + ASSERT(what); + + CLIST_FOR_EACH_DELSAFE(simp_node *, i, where->substreams, tmp) + if (i->p == what) + { + clist_remove((cnode*)i); + xfree(i); + cnt++; + } + return cnt; +} + +/* Return a pointer to a new stream with no handler and an empty substream list. */ +struct log_stream *ls_new(void) +{ + struct log_stream *l; + int index; + + /* initialize the array if not initialized already */ + if (unlikely(ls_initialized==0)) + ls_init_module(); + + /* there is no closed stream -- allocate a new one */ + if (ls_streams_free==-1) + { + /* check the size of the pointer array */ + lsbuf_grow(&log_streams, log_streams_after+1); + ls_streams_free = log_streams_after++; + log_streams.ptr[ls_streams_free] = xmalloc(sizeof(struct log_stream)); + log_streams.ptr[ls_streams_free]->idata = -1; + log_streams.ptr[ls_streams_free]->regnum = -1; + } + + ASSERT(ls_streams_free>=0); + + /* initialize the stream */ + index = ls_streams_free; + l = log_streams.ptr[index]; + ls_streams_free = l->idata; + memset(l, 0, sizeof(struct log_stream)); + l->levels = LS_ALL_LEVELS; + l->regnum = LS_SET_STRNUM(index); + l->substreams.head.next = &(l->substreams.head); + l->substreams.head.prev = &(l->substreams.head); + return l; +} + +/* Close and remember given log_stream */ +/* does not affect substreams, but frees the .substreams list */ +void ls_close(struct log_stream *ls) +{ + void *tmp; + ASSERT(ls); + + /* xfree() all the simp_nodes from substreams */ + CLIST_FOR_EACH_DELSAFE(simp_node *, i, ls->substreams, tmp) + { + clist_remove((cnode*)i); + xfree(i); + } + + /* close and remember the stream */ + if (ls->close!=NULL) + ls->close(ls); + ls->idata = ls_streams_free; + ls_streams_free = LS_GET_STRNUM(ls->regnum); + ls->regnum = -1; +} diff --git a/ucw/log-syslog.c b/ucw/log-syslog.c new file mode 100644 index 00000000..9883f789 --- /dev/null +++ b/ucw/log-syslog.c @@ -0,0 +1,68 @@ +/* + * UCW Library -- Logging to Syslog + * + * (c) 2009 Martin Mares + * (c) 2008 Tomas Gavenciak + * + * 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 + +/* destructor for syslog logs */ +static void ls_syslog_close(struct log_stream *ls) +{ + ASSERT(ls); + if(ls->name) + xfree(ls->name); +} + +/* convert severity level to syslog constants */ +static int ls_syslog_convert_level(int level) +{ + switch(level) + { + case L_DEBUG: return LOG_DEBUG; + case L_INFO: return LOG_INFO; + case L_INFO_R: return LOG_INFO; + case L_WARN: return LOG_WARNING; + case L_WARN_R: return LOG_WARNING; + case L_ERROR: return LOG_ERR; + case L_ERROR_R: return LOG_ERR; + case L_FATAL: return LOG_CRIT; + default: return LOG_NOTICE; + } +} + +/* simple syslog write handler */ +static int ls_syslog_handler(struct log_stream *ls, const char *m, u32 flags) +{ + int prio; + ASSERT(ls); + ASSERT(m); + + prio = ls_syslog_convert_level(LS_GET_LEVEL(flags)) | (ls->idata); + if (ls->name) + syslog(prio | (ls->idata), "%s: %s", ls->name, m); + else + syslog(prio | (ls->idata), "%s", m); + return 0; +} + +/* assign log to a syslog facility */ +/* initialize with no formatting (syslog adds these inforamtion) */ +/* name is optional prefix (NULL for none) */ +struct log_stream *ls_syslog_new(int facility, const char *name) +{ + struct log_stream *ls=ls_new(); + if (name) ls->name = xstrdup(name); + ls->idata = facility; + ls->msgfmt = LSFMT_NONE; + ls->handler = ls_syslog_handler; + ls->close = ls_syslog_close; + return ls; +} diff --git a/ucw/log.c b/ucw/log.c index c312b9bf..44be6302 100644 --- a/ucw/log.c +++ b/ucw/log.c @@ -1,13 +1,16 @@ /* * UCW Library -- Logging * - * (c) 1997--2008 Martin Mares + * (c) 1997--2009 Martin Mares + * (c) 2008 Tomas Gavenciak * * 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/simple-lists.h" #include #include @@ -16,86 +19,227 @@ #include #include #include +#include static char log_progname[32]; -char *log_filename; char *log_title; int log_pid; -int log_precise_timings; void (*log_die_hook)(void); -void (*log_switch_hook)(struct tm *tm); -void -vmsg(unsigned int cat, const char *fmt, va_list args) +// FIXME: duplicate? +static int ls_default_handler(struct log_stream* ls, const char *m, u32 cat UNUSED) +{ + int len = strlen(m); + int r = write(ls->idata, m, len); + /* TODO: check the errors here? */ + if (r!=len) + return errno; + return 0; +} + +/* the default logger */ +const struct log_stream ls_default_log={ + .name = "fd2", .idata = 2, .pdata = NULL, .regnum = 0, + .handler = ls_default_handler, + .levels = LS_ALL_LEVELS, + .msgfmt = LSFMT_DEFAULT, + // empty clist + .substreams.head.next = (cnode *) &ls_default_log.substreams.head, + .substreams.head.prev = (cnode *) &ls_default_log.substreams.head, +}; + +/*** Registry of streams and their identifiers ***/ + +/* The growing array of pointers to log_streams. */ +struct lsbuf_t log_streams; + +/* the first never-used index in ls_streams.ptr */ +int log_streams_after = 0; + +/* get a stream by its LS_SET_STRNUM() */ +/* returns NULL for free/invalid numbers */ +/* defaults to ls_default_stream when stream number 0 closed */ +struct log_stream *log_stream_by_flags(uns flags) +{ + /* get the real number */ + int n = LS_GET_STRNUM(flags); + if ((n<0) || (n>=log_streams_after) || (log_streams.ptr[n]->regnum==-1) ) + { + if (n==0) + return (struct log_stream *)&ls_default_log; + else + return NULL; + } + return log_streams.ptr[n]; +} + +/*** Logging ***/ + +void vmsg(unsigned int cat, const char *fmt, va_list args) { struct timeval tv; int have_tm = 0; struct tm tm; - byte *buf, *p; - int buflen = 256; - int l, l0, r; va_list args2; + char stime[24]; + char sutime[12]; + char *buf,*p; + int len; + struct log_stream *ls=log_stream_by_flags(cat); + + /* Check the stream existence */ + if(!ls) + { + msg((LS_INTERNAL_MASK&cat)|L_WARN, "No log_stream with number %d! Logging to the default log.", + LS_GET_STRNUM(cat)); + ls=(struct log_stream *)&ls_default_log; + } - if (!(cat & L_SIGHANDLER)) + /* get the time */ + if (!(cat&LSFLAG_SIGHANDLER)) { /* CAVEAT: These calls are not safe in signal handlers. */ gettimeofday(&tv, NULL); if (localtime_r(&tv.tv_sec, &tm)) have_tm = 1; - if (log_switch_hook) - log_switch_hook(&tm); } - while (1) + /* generate time strings */ + if (have_tm) { - p = buf = alloca(buflen); - *p++ = cat & 0xff; - if (have_tm) + strftime(stime, 24, "%Y-%m-%d %H:%M:%S", &tm); + snprintf(sutime, 12, ".%06d", (int)tv.tv_usec); + } + else + { + snprintf(stime, 24, "\?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?"); + snprintf(sutime, 12, ".\?\?\?\?\?\?"); + } + + /* generate the message string */ + va_copy(args2, args); + /* WARN: this may be C99 specefic */ + len = vsnprintf(NULL, 0, fmt, args2); + va_end(args2); + buf = xmalloc(len+2); + vsnprintf(buf, len+1, fmt, args); + + /* remove non-printable characters and newlines */ + p=buf; + while (*p) + { + if (*p < 0x20 && *p != '\t') + *p = 0x7f; + p++; + } + + /* pass the message to the log_stream */ + if(ls_passmsg(0, ls, stime, sutime, buf, cat)) + { + /* error (such as looping) occured */ + ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, buf, cat); + } + + xfree(buf); +} + +/* process a message (string) */ +/* depth prevents undetected looping */ +/* returns 1 in case of loop detection or other fatal error + * 0 otherwise */ +int ls_passmsg(int depth, struct log_stream *ls, const char *stime, const char *sutime, const char *m, u32 cat) +{ + ASSERT(ls); + + /* Check recursion depth */ + if( depth > LS_MAX_DEPTH ) + { + ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, + "Loop in the log_stream system detected.", L_ERROR | (cat&LS_INTERNAL_MASK) ); + return 1; + } + + /* Filter by level and filter hook */ + if(!( (1<levels )) return 0; + if( ls->filter ) + if( ls->filter(ls, m, cat) != 0 ) return 0; + + /* pass message to substreams */ + CLIST_FOR_EACH(simp_node *, s, ls->substreams) + { + if (ls_passmsg(depth+1, (struct log_stream*)(s->p), stime, sutime, m, cat)) + return 1; + } + + /* Prepare for handler */ + if(ls->handler) + { + int len = strlen(m) + strlen(stime) + strlen(sutime) + 32; + /* SHOULD be enough for all information, but beware */ + if (log_title) len += strlen(log_title); + if (ls->name) len += strlen(ls->name); + char *buf=xmalloc(len); + char *p=buf; + + /* Level (2 chars) */ + if(ls->msgfmt & LSFMT_LEVEL) { - p += strftime(p, buflen, " %Y-%m-%d %H:%M:%S", &tm); - if (log_precise_timings) - p += sprintf(p, ".%06d", (int)tv.tv_usec); + *p++=LS_LEVEL_LETTER(LS_GET_LEVEL(cat)); + *p++=' '; } - else + + /* Time (|stime| + |sutime| + 1 chars) */ + if(ls->msgfmt & LSFMT_TIME) { - p += sprintf(p, " \?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?"); - if (log_precise_timings) - p += sprintf(p, ".\?\?\?\?\?\?"); + char *q = (char *)stime; + + while(*q) + *p++=*q++; + if(ls->msgfmt & LSFMT_USEC) + { + q = (char *)sutime; + while(*q) + *p++=*q++; + } + *p++=' '; } - *p++ = ' '; - if (log_title) + + /* process name, PID ( |log_title| + 6 + (|PID|<=10) chars ) */ + if((ls->msgfmt & LSFMT_TITLE) && log_title) { - if (log_pid) - p += sprintf(p, "[%s (%d)] ", log_title, log_pid); + if(ls->msgfmt & LSFMT_PID) + p += sprintf(p, "[%s (%d)] ", log_title, getpid()); else p += sprintf(p, "[%s] ", log_title); } else { - if (log_pid) - p += sprintf(p, "[%d] ", log_pid); + if(ls->msgfmt & LSFMT_PID) + p += sprintf(p, "[%d] ", getpid()); } - l0 = p - buf + 1; - r = buflen - l0; - va_copy(args2, args); - l = vsnprintf(p, r, fmt, args2); - va_end(args2); - if (l < 0) - l = r; - else if (l < r) + + /* log_stream name ( |ls->name| + 4 chars ) */ + if(ls->msgfmt & LSFMT_LOGNAME) { - while (*p) - { - if (*p < 0x20 && *p != '\t') - *p = 0x7f; - p++; - } - *p = '\n'; - write(2, buf, l + l0); - return; + if(ls->name) + p += sprintf(p, "<%s> ", ls->name); + else + p += sprintf(p, " "); + } + + /* finish the string and call the handler ( |m| + 1 chars ) */ + { + char *q = (char *)m; + + while(*q) + *p++=*q++; + *p++ = '\n'; + *p++ = '\0'; + ls->handler(ls, buf, cat); } - buflen = l + l0 + 1; + xfree(buf); } + return 0; } void @@ -159,3 +303,14 @@ log_init(const char *argv0) log_title = log_progname; } } + +#ifdef TEST + +int main(void) +{ + // ls_default_log.msgfmt |= LSFMT_USEC; + msg(L_INFO, "Brum!"); + return 0; +} + +#endif diff --git a/ucw/logstream.h b/ucw/log.h similarity index 92% rename from ucw/logstream.h rename to ucw/log.h index 67ac4d27..cbc018e9 100644 --- a/ucw/logstream.h +++ b/ucw/log.h @@ -1,15 +1,15 @@ /* * UCW Library -- Logging * + * (c) 1997--2009 Martin Mares * (c) 2008 Tomas Gavenciak - * (c) 2009 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. */ -#ifndef _UCW_LOGSTREAM_H_ -#define _UCW_LOGSTREAM_H_ +#ifndef _UCW_LOG_H_ +#define _UCW_LOG_H_ #include "ucw/clists.h" @@ -65,18 +65,6 @@ enum ls_fmt LSFMT_DEFAULT=LSFMT_LEVEL+LSFMT_TIME }; -enum ls_levels -{ - L_DEBUG=0, /* 'D' - Debugging messages */ - L_INFO, /* 'I' - Informational */ - L_INFO_R, /* 'i' - xxx_R are messages caused by external events */ - L_WARN, /* 'W' - Warning */ - L_WARN_R, /* 'w' */ - L_ERROR, /* 'E' - Error, but non-critical */ - L_ERROR_R, /* 'e' */ - L_FATAL, /* '!' - Error, from die() */ -}; - /* Mask of containing all existing levels. */ #define LS_ALL_LEVELS 0xfff @@ -156,7 +144,7 @@ int ls_rm_substream(struct log_stream *where, struct log_stream *what); /* get a stream by its number (regnum) */ /* returns NULL for free numbers */ /* defaults to ls_default_stream for 0 when stream number 0 not set */ -struct log_stream *ls_bynum(int num); +struct log_stream *log_stream_by_flags(uns flags); /* The proposed alternative to original vmsg() */ void ls_vmsg(unsigned int cat, const char *fmt, va_list args); @@ -177,6 +165,14 @@ int ls_passmsg(int depth, struct log_stream *ls, const char *stime, const char * /* Maximal depth of ls_passmsg recursion */ #define LS_MAX_DEPTH 64 +/* Define an array (growing buffer) for pointers to log_streams. */ +#define GBUF_TYPE struct log_stream* +#define GBUF_PREFIX(x) lsbuf_##x +#include "ucw/gbuf.h" + +extern struct lsbuf_t log_streams; +extern int log_streams_after; + /********* Individual handler types (constructors, handlers, destructors) */ /**** standard (filedes) files */ diff --git a/ucw/logstream.c b/ucw/logstream.c deleted file mode 100644 index 97ef9d39..00000000 --- a/ucw/logstream.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * UCW Library -- Logging - * - * (c) 2008 Tomas Gavenciak - * (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/clists.h" -#include "ucw/simple-lists.h" -#include "ucw/logstream.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* forward declaration */ -static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat); - -/* the deafult logger */ -const struct log_stream ls_default_log={ - .name = "fd2", .idata = 2, .pdata = NULL, .regnum = 0, - .handler = ls_fdfile_handler, - .levels = LS_ALL_LEVELS, - .msgfmt = LSFMT_DEFAULT, - // empty clist - .substreams.head.next = (cnode *) &ls_default_log.substreams.head, - .substreams.head.prev = (cnode *) &ls_default_log.substreams.head, -}; - -/* user de/allocated program/process name for use in the logsystem (for LSFMT_TITLE) */ -char *ls_title = NULL; - -/* Define an array (growing buffer) for pointers to log_streams. */ -#define GBUF_TYPE struct log_stream* -#define GBUF_PREFIX(x) lsbuf_##x -#include "ucw/gbuf.h" - -/* Flag indicating initialization of the module */ -static int ls_initialized = 0; - -/* The growing array of pointers to log_streams. */ -static struct lsbuf_t ls_streams; - -/* The head of the list of freed log_streams indexes in ls_streams.ptr (-1 if none free). - * Freed positions in ls_streams.ptr are connected into a linked list in the following way: - * ls_streams.ptr[ls_streams_free].idata is the index of next freed position (or -1) */ -int ls_streams_free = -1; - -/* the first never-used index in ls_streams.ptr */ -int ls_streams_after = 0; - -/* Initialize the logstream module. - * It is not neccessary to call this explicitely as it is called by - * the first ls_new() (for backward compatibility and ease of use). */ -static void ls_init_module(void) -{ - if (ls_initialized) return; - - /* create the grow array */ - lsbuf_init(&ls_streams); - lsbuf_set_size(&ls_streams, LS_INIT_STREAMS); - - bzero(ls_streams.ptr, sizeof(struct log_stream*) * (ls_streams.len)); - ls_streams_free = -1; - - ls_initialized = 1; - - /* init the default stream (0) as forwarder to fd2 */ - struct log_stream *ls = ls_new(); - ASSERT(ls == ls_streams.ptr[0]); - ASSERT(ls->regnum == 0); - ls->name = "default"; - ls_add_substream(ls, (struct log_stream *) &ls_default_log); - - /* log this */ - ls_msg(L_DEBUG, "logstream module initialized."); -} - -/* close all open streams, un-initialize the module, free all memory, - * use only ls_default_log */ -void ls_close_all(void) -{ - int i; - - if (!ls_initialized) return; - - for (i=0; iregnum>=0) - ls_close(ls_streams.ptr[i]); - xfree(ls_streams.ptr[i]); - } - - /* set to the default state */ - lsbuf_done(&ls_streams); - ls_streams_after=0; - ls_streams_free=-1; - ls_initialized = 0; -} - -/* add a new substream, malloc()-ate a new simp_node */ -void ls_add_substream(struct log_stream *where, struct log_stream *what) -{ - ASSERT(where); - ASSERT(what); - - simp_node *n = xmalloc(sizeof(simp_node)); - n->p = what; - clist_add_tail(&(where->substreams), (cnode*)n); -} - -/* remove all occurences of a substream, free() the simp_node */ -/* return number of deleted entries */ -int ls_rm_substream(struct log_stream *where, struct log_stream *what) -{ - void *tmp; - int cnt=0; - ASSERT(where); - ASSERT(what); - - CLIST_FOR_EACH_DELSAFE(simp_node *, i, where->substreams, tmp) - if (i->p == what) - { - clist_remove((cnode*)i); - xfree(i); - cnt++; - } - return cnt; -} - -/* Return a pointer to a new stream with no handler and an empty substream list. */ -struct log_stream *ls_new(void) -{ - struct log_stream *l; - int index; - - /* initialize the array if not initialized already */ - if (unlikely(ls_initialized==0)) - ls_init_module(); - - /* there is no closed stream -- allocate a new one */ - if (ls_streams_free==-1) - { - /* check the size of the pointer array */ - lsbuf_grow(&ls_streams, ls_streams_after+1); - ls_streams_free = ls_streams_after++; - ls_streams.ptr[ls_streams_free] = xmalloc(sizeof(struct log_stream)); - ls_streams.ptr[ls_streams_free]->idata = -1; - ls_streams.ptr[ls_streams_free]->regnum = -1; - } - - ASSERT(ls_streams_free>=0); - - /* initialize the stream */ - index = ls_streams_free; - l = ls_streams.ptr[index]; - ls_streams_free = l->idata; - memset(l, 0, sizeof(struct log_stream)); - l->levels = LS_ALL_LEVELS; - l->regnum = LS_SET_STRNUM(index); - l->substreams.head.next = &(l->substreams.head); - l->substreams.head.prev = &(l->substreams.head); - return l; -} - -/* Close and remember given log_stream */ -/* does not affect substreams, but frees the .substreams list */ -void ls_close(struct log_stream *ls) -{ - void *tmp; - ASSERT(ls); - - /* xfree() all the simp_nodes from substreams */ - CLIST_FOR_EACH_DELSAFE(simp_node *, i, ls->substreams, tmp) - { - clist_remove((cnode*)i); - xfree(i); - } - - /* close and remember the stream */ - if (ls->close!=NULL) - ls->close(ls); - ls->idata = ls_streams_free; - ls_streams_free = LS_GET_STRNUM(ls->regnum); - ls->regnum = -1; -} - -/* get a stream by its LS_SET_STRNUM() */ -/* returns NULL for free/invalid numbers */ -/* defaults to ls_default_stream when stream number 0 closed */ -struct log_stream *ls_bynum(int num) -{ - /* get the real number */ - int n = LS_GET_STRNUM(num); - if ((n<0) || (n>=ls_streams_after) || (ls_streams.ptr[n]->regnum==-1) ) - { - if (n==0) - return (struct log_stream *)&ls_default_log; - else - return NULL; - } - return ls_streams.ptr[n]; -} - -/* The proposed alternative to original vmsg() */ -void ls_vmsg(unsigned int cat, const char *fmt, va_list args) -{ - struct timeval tv; - int have_tm = 0; - struct tm tm; - va_list args2; - char stime[24]; - char sutime[12]; - char *buf,*p; - int len; - struct log_stream *ls=ls_bynum(cat); - - /* Check the stream existence */ - if(!ls) - { - ls_msg((LS_INTERNAL_MASK&cat)|L_WARN, "No log_stream with number %d! Logging to the default log.", - LS_GET_STRNUM(cat)); - ls=(struct log_stream *)&ls_default_log; - } - - /* get the time */ - if (!(cat&LSFLAG_SIGHANDLER)) - { - /* CAVEAT: These calls are not safe in signal handlers. */ - gettimeofday(&tv, NULL); - if (localtime_r(&tv.tv_sec, &tm)) - have_tm = 1; - } - - /* generate time strings */ - if (have_tm) - { - strftime(stime, 24, "%Y-%m-%d %H:%M:%S", &tm); - snprintf(sutime, 12, ".%06d", (int)tv.tv_usec); - } - else - { - snprintf(stime, 24, "\?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?"); - snprintf(sutime, 12, ".\?\?\?\?\?\?"); - } - - /* generate the message string */ - va_copy(args2, args); - /* WARN: this may be C99 specefic */ - len = vsnprintf(NULL, 0, fmt, args2); - va_end(args2); - buf = xmalloc(len+2); - vsnprintf(buf, len+1, fmt, args); - - /* remove non-printable characters and newlines */ - p=buf; - while (*p) - { - if (*p < 0x20 && *p != '\t') - *p = 0x7f; - p++; - } - - /* pass the message to the log_stream */ - if(ls_passmsg(0, ls, stime, sutime, buf, cat)) - { - /* error (such as looping) occured */ - ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, buf, cat); - } - - xfree(buf); -} - -/* The proposed alternative to original msg() */ -void ls_msg(unsigned int cat, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - ls_vmsg(cat, fmt, args); - va_end(args); -} - -/* The proposed alternative to original die() */ -void ls_die(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - ls_vmsg(L_FATAL, fmt, args); - va_end(args); -///// why this? -// if (log_die_hook) -// log_die_hook(); -#ifdef DEBUG_DIE_BY_ABORT - abort(); -#else - exit(1); -#endif -} - -/* process a message (string) */ -/* depth prevents undetected looping */ -/* returns 1 in case of loop detection or other fatal error - * 0 otherwise */ -int ls_passmsg(int depth, struct log_stream *ls, const char *stime, const char *sutime, const char *m, u32 cat) -{ - ASSERT(ls); - - /* Check recursion depth */ - if( depth > LS_MAX_DEPTH ) - { - ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, - "Loop in the log_stream system detected.", L_ERROR | (cat&LS_INTERNAL_MASK) ); - return 1; - } - - /* Filter by level and filter hook */ - if(!( (1<levels )) return 0; - if( ls->filter ) - if( ls->filter(ls, m, cat) != 0 ) return 0; - - /* pass message to substreams */ - CLIST_FOR_EACH(simp_node *, s, ls->substreams) - { - if (ls_passmsg(depth+1, (struct log_stream*)(s->p), stime, sutime, m, cat)) - return 1; - } - - /* Prepare for handler */ - if(ls->handler) - { - int len = strlen(m) + strlen(stime) + strlen(sutime) + 32; - /* SHOULD be enough for all information, but beware */ - if (ls_title) len += strlen(ls_title); - if (ls->name) len += strlen(ls->name); - char *buf=xmalloc(len); - char *p=buf; - - /* Level (2 chars) */ - if(ls->msgfmt & LSFMT_LEVEL) - { - *p++=LS_LEVEL_LETTER(LS_GET_LEVEL(cat)); - *p++=' '; - } - - /* Time (|stime| + |sutime| + 1 chars) */ - if(ls->msgfmt & LSFMT_TIME) - { - char *q = (char *)stime; - - while(*q) - *p++=*q++; - if(ls->msgfmt & LSFMT_USEC) - { - q = (char *)sutime; - while(*q) - *p++=*q++; - } - *p++=' '; - } - - /* process name, PID ( |ls_title| + 6 + (|PID|<=10) chars ) */ - if((ls->msgfmt & LSFMT_TITLE) && ls_title) - { - if(ls->msgfmt & LSFMT_PID) - p += sprintf(p, "[%s (%d)] ", ls_title, getpid()); - else - p += sprintf(p, "[%s] ", ls_title); - } - else - { - if(ls->msgfmt & LSFMT_PID) - p += sprintf(p, "[%d] ", getpid()); - } - - /* log_stream name ( |ls->name| + 4 chars ) */ - if(ls->msgfmt & LSFMT_LOGNAME) - { - if(ls->name) - p += sprintf(p, "<%s> ", ls->name); - else - p += sprintf(p, " "); - } - - /* finish the string and call the handler ( |m| + 1 chars ) */ - { - char *q = (char *)m; - - while(*q) - *p++=*q++; - *p++ = '\n'; - *p++ = '\0'; - ls->handler(ls, buf, cat); - } - xfree(buf); - } - return 0; -} - - -/********** log types */ - - -/**** standard (filedes) files */ - -/* destructor for standard files */ -static void ls_fdfile_close(struct log_stream *ls) -{ - ASSERT(ls); - close(ls->idata); - if(ls->name) - xfree(ls->name); -} - -/* handler for standard files */ -static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat UNUSED) -{ - int len = strlen(m); - int r = write(ls->idata, m, len); - /* TODO: check the errors here? */ - if (r!=len) - return errno; - return 0; -} - -/* assign log to a file descriptor */ -/* initialize with the default formatting, does NOT close the descriptor */ -struct log_stream *ls_fdfile_new(int fd) -{ - struct log_stream *ls=ls_new(); - ls->idata=fd; - ls->msgfmt=LSFMT_DEFAULT; - ls->handler=ls_fdfile_handler; - return ls; -} - -/* open() a file (append mode) */ -/* initialize with the default formatting */ -struct log_stream *ls_file_new(const char *path) -{ - struct log_stream *ls; - int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0666); - if (fd<0) - { - ls_msg(L_ERROR, "Opening logfile '%s' failed: %m.", path); - return NULL; - } - ls = ls_new(); - ls->name = xstrdup(path); - ls->idata = fd; - ls->msgfmt = LSFMT_DEFAULT; - ls->handler = ls_fdfile_handler; - ls->close = ls_fdfile_close; - return ls; -} - - -/**** syslog */ - -/* destructor for syslog logs */ -static void ls_syslog_close(struct log_stream *ls) -{ - ASSERT(ls); - if(ls->name) - xfree(ls->name); -} - -/* convert severity level to syslog constants */ -static int ls_syslog_convert_level(int level) -{ - switch(level) - { - case L_DEBUG: return LOG_DEBUG; - case L_INFO: return LOG_INFO; - case L_INFO_R: return LOG_INFO; - case L_WARN: return LOG_WARNING; - case L_WARN_R: return LOG_WARNING; - case L_ERROR: return LOG_ERR; - case L_ERROR_R: return LOG_ERR; - case L_FATAL: return LOG_CRIT; - default: return LOG_NOTICE; - } -} - -/* simple syslog write handler */ -static int ls_syslog_handler(struct log_stream *ls, const char *m, u32 flags) -{ - int prio; - ASSERT(ls); - ASSERT(m); - - prio = ls_syslog_convert_level(LS_GET_LEVEL(flags)) | (ls->idata); - if (ls->name) - syslog(prio | (ls->idata), "%s: %s", ls->name, m); - else - syslog(prio | (ls->idata), "%s", m); - return 0; -} - -/* assign log to a syslog facility */ -/* initialize with no formatting (syslog adds these inforamtion) */ -/* name is optional prefix (NULL for none) */ -struct log_stream *ls_syslog_new(int facility, const char *name) -{ - struct log_stream *ls=ls_new(); - if (name) ls->name = xstrdup(name); - ls->idata = facility; - ls->msgfmt = LSFMT_NONE; - ls->handler = ls_syslog_handler; - ls->close = ls_syslog_close; - return ls; -} - -#ifdef TEST - -int main(void) -{ - ls_msg(L_INFO, "Brum!"); - return 0; -} - -#endif -- 2.39.2