X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Flog-file.c;h=efa0665f99bde467954390a085f267b6687e502c;hb=0f73a9b90ec53017512c34f7dab56be3a50d87b1;hp=7bfeaa6822280946c46f07556e4c91be521cfb5b;hpb=031256ad2e123eec58521f8e3eb9496c197641d2;p=libucw.git diff --git a/ucw/log-file.c b/ucw/log-file.c index 7bfeaa68..efa0665f 100644 --- a/ucw/log-file.c +++ b/ucw/log-file.c @@ -1,97 +1,162 @@ /* - * 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/log-internal.h" #include "ucw/lfs.h" #include "ucw/threads.h" +#include "ucw/simple-lists.h" #include #include #include #include #include +#include + +struct file_stream { + struct log_stream ls; // ls.name is the current name of the log file + int fd; + uns flags; // FF_xxx + char *orig_name; // Original name with strftime escapes +}; + +#define MAX_EXPAND 64 // Maximum size of expansion of strftime escapes -static char *log_name_patt; -static int log_params; -static int log_filename_size; static int log_switch_nest; +static void +do_log_reopen(struct file_stream *fs, const char *name) +{ + int fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (fd < 0) + die("Unable to open log file %s: %m", name); + if (fs->fd >= 0) + close(fs->fd); + fs->fd = fd; + if (fs->flags & FF_FD2_FOLLOWS) + dup2(fd, 2); + if (fs->ls.name) + { + xfree(fs->ls.name); + fs->ls.name = NULL; // We have to keep the stream consistent -- die() below can invoke logging + } + fs->ls.name = xstrdup(name); +} + static int -do_log_switch(struct tm *tm) +do_log_switch(struct file_stream *fs, struct tm *tm) { - int fd, l; - char name[log_filename_size]; + if (!(fs->flags & FF_FORMAT_NAME)) + { + if (fs->fd >= 0) + return 1; + else + { + do_log_reopen(fs, fs->orig_name); + return 1; + } + } + + int buflen = strlen(fs->orig_name) + MAX_EXPAND; + char name[buflen]; int switched = 0; - if (!log_name_patt || - log_filename[0] && !log_params) - return 0; ucwlib_lock(); - log_switch_nest++; - l = strftime(name, log_filename_size, log_name_patt, tm); - if (l < 0 || l >= log_filename_size) - die("Error formatting log file name: %m"); - if (strcmp(name, log_filename)) + if (!log_switch_nest) // Avoid infinite loops if we die when switching logs { - strcpy(log_filename, name); - fd = sh_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666); - if (fd < 0) - die("Unable to open log file %s: %m", name); - dup2(fd, 2); - close(fd); - switched = 1; + log_switch_nest++; + int l = strftime(name, buflen, fs->orig_name, tm); + if (l < 0 || l >= buflen) + die("Error formatting log file name: %m"); + if (!fs->ls.name || strcmp(name, fs->ls.name)) + { + do_log_reopen(fs, name); + switched = 1; + } + log_switch_nest--; } - log_switch_nest--; ucwlib_unlock(); return switched; } -int -log_switch(void) +static void +file_close(struct log_stream *ls) { - time_t tim = time(NULL); - return do_log_switch(localtime(&tim)); + struct file_stream *fs = (struct file_stream *) ls; + if ((fs->flags & FF_CLOSE_FD) && fs->fd >= 0) + close(fs->fd); + xfree(fs->ls.name); + xfree(fs->orig_name); } -static void -internal_log_switch(struct tm *tm) +static int +file_handler(struct log_stream *ls, struct log_msg *m) { - if (!log_switch_nest) - do_log_switch(tm); + struct file_stream *fs = (struct file_stream *) ls; + if ((fs->flags & FF_FORMAT_NAME) && m->tm) + do_log_switch(fs, m->tm); + + int r = write(fs->fd, m->m, m->m_len); + return ((r < 0) ? errno : 0); } -void -log_file(const char *name) +struct log_stream * +log_new_fd(int fd, uns flags) { - if (name) - { - if (log_name_patt) - xfree(log_name_patt); - if (log_filename) - { - xfree(log_filename); - log_filename = NULL; - } - log_name_patt = xstrdup(name); - log_params = !!strchr(name, '%'); - log_filename_size = strlen(name) + 64; /* 63 is an upper bound on expansion of % escapes */ - log_filename = xmalloc(log_filename_size); - log_filename[0] = 0; - log_switch(); - log_switch_hook = internal_log_switch; - } + struct log_stream *ls = log_new_stream(sizeof(struct file_stream)); + struct file_stream *fs = (struct file_stream *) ls; + fs->fd = fd; + fs->flags = flags; + ls->msgfmt = LSFMT_DEFAULT; + ls->handler = file_handler; + ls->close = file_close; + ls->name = xmalloc(16); + snprintf(ls->name, 16, "fd%d", fd); + return ls; } -void -log_fork(void) +struct log_stream * +log_new_file(const char *path, uns flags) +{ + struct log_stream *ls = log_new_stream(sizeof(struct file_stream)); + struct file_stream *fs = (struct file_stream *) ls; + fs->fd = -1; + fs->orig_name = xstrdup(path); + if (strchr(path, '%')) + fs->flags = FF_FORMAT_NAME; + fs->flags |= FF_CLOSE_FD | flags; + ls->msgfmt = LSFMT_DEFAULT; + ls->handler = file_handler; + ls->close = file_close; + + time_t now = time(NULL); + struct tm *tm = localtime(&now); + ASSERT(tm); + do_log_switch(fs, tm); // die()'s on errors + return ls; +} + +int +log_switch(void) { - log_pid = getpid(); + time_t now = time(NULL); + struct tm *tm = localtime(&now); + ASSERT(tm); + + int switched = 0; + for (int i=0; i < log_streams_after; i++) + if (log_streams.ptr[i]->handler == file_handler) + switched |= do_log_switch((struct file_stream *) log_streams.ptr[i], tm); + return switched; } void @@ -107,14 +172,30 @@ log_switch_enable(void) log_switch_nest--; } +void +log_file(const char *name) +{ + if (!name) + return; + + struct log_stream *ls = log_new_file(name, FF_FD2_FOLLOWS); + struct log_stream *def = log_stream_by_flags(0); + log_rm_substream(def, NULL); + log_add_substream(def, ls); + log_close_stream(ls); +} + #ifdef TEST int main(int argc, char **argv) { log_init(argv[0]); log_file("/proc/self/fd/1"); + // struct log_stream *ls = log_new_fd(1, 0); + // struct log_stream *ls = log_new_file("/tmp/quork-%Y%m%d-%H%M%S", 0); for (int i=1; i