Added snprintf/vsnprintf wrappers for systems that don't have
[yaz-moved-to-github.git] / src / log.c
index d4973f2..351c0bd 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,8 +1,8 @@
 /*
- * Copyright (C) 1995-2006, Index Data ApS
+ * Copyright (C) 1995-2007, Index Data ApS
  * See the file LICENSE for details.
  *
- * $Id: log.c,v 1.38 2006-07-31 10:05:04 adam Exp $
+ * $Id: log.c,v 1.48 2007-02-23 10:15:01 adam Exp $
  */
 
 /**
@@ -26,6 +26,7 @@
 #include <pth.h>
 #endif
 
+#include <sys/stat.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <time.h>
 #include <yaz/nmem.h>
 #include <yaz/log.h>
+#include <yaz/snprintf.h>
 #include <yaz/xmalloc.h>
 
 static NMEM_MUTEX log_mutex = 0;
-static int mutex_init_flag = 0; /* not yet initialized */
 
 #define HAS_STRERROR 1
 
@@ -55,18 +56,9 @@ char *strerror(int n)
 #endif
 
 
-static int default_log_level() {
-    char *env = getenv("YAZ_LOG");
-    if (env != 0)
-        return yaz_log_mask_str_x(env, YLOG_DEFAULT_LEVEL);
-    else
-        return YLOG_DEFAULT_LEVEL;
-}
-
+static int l_level = YLOG_DEFAULT_LEVEL;
 
-static int l_level = -1;        /* will be set from default_log_level() */
-
-enum l_file_type {  use_stderr, use_none, use_file };
+enum l_file_type { use_stderr, use_none, use_file };
 static enum l_file_type yaz_file_type = use_stderr;
 static FILE *yaz_global_log_file = NULL;
 
@@ -90,8 +82,10 @@ static char l_new_default_format[] = "%Y%m%d-%H%M%S";
 static char l_custom_format[TIMEFORMAT_LEN] = "";
 static char *l_actual_format = l_old_default_format;
 
-/** l_max_size tells when to rotate the log. Default to 1 GB */
-static const int l_def_max_size = 1024*1024*1024;
+/** l_max_size tells when to rotate the log. Default is 1 GB 
+    This is almost the same as never, but it saves applications in the
+    case of 2 or 4 GB file size limits..
+ */
 static int l_max_size = 1024*1024*1024;
 
 #define MAX_MASK_NAMES 35   /* 32 bits plus a few combo names */
@@ -120,14 +114,23 @@ static struct {
 
 static unsigned int next_log_bit = YLOG_LAST_BIT<<1; /* first dynamic bit */
 
-static void init_mutex()
+static void internal_log_init(void)
 {
+    static int mutex_init_flag = 0; /* not yet initialized */
+    char *env;
+
     if (mutex_init_flag)
         return;
+    mutex_init_flag = 1; /* here, 'cause nmem_mutex_create may call yaz_log */
+
     nmem_mutex_create(&log_mutex);
-    mutex_init_flag = 1;
+
+    env = getenv("YAZ_LOG");
+    if (env)
+        l_level = yaz_log_mask_str_x(env, l_level);
 }
 
+
 FILE *yaz_log_file(void)
 {
     FILE *f = 0;
@@ -140,7 +143,7 @@ FILE *yaz_log_file(void)
     return f;
 }
 
-void yaz_log_close()
+void yaz_log_close(void)
 {
     if (yaz_file_type == use_file && yaz_global_log_file)
     {
@@ -151,7 +154,7 @@ void yaz_log_close()
 
 void yaz_log_init_file(const char *fname)
 {
-    init_mutex();
+    internal_log_init();
 
     yaz_log_close();
     if (fname)
@@ -173,26 +176,42 @@ void yaz_log_init_file(const char *fname)
 
 static void rotate_log(const char *cur_fname)
 {
-    char newname[512];
-    strncpy(newname, cur_fname, 509);
-    newname[509] = '\0'; /* make sure it is terminated */
-    strcat(newname, ".1");
+    int i;
+
 #ifdef WIN32
     /* windows can't rename a file if it is open */
-    fclose(yaz_global_log_file);
-    yaz_global_log_file = 0;
-    MoveFileEx(cur_fname, newname, MOVEFILE_REPLACE_EXISTING);
+    yaz_log_close();
+#endif
+    for (i = 0; i<9; i++)
+    {
+        char fname_str[FILENAME_MAX];
+        struct stat stat_buf;
+
+        sprintf(fname_str, "%s.%d", cur_fname, i);
+        if (stat(fname_str, &stat_buf) != 0)
+            break;
+    }
+    for (; i >= 0; --i)
+    {
+        char fname_str[2][FILENAME_MAX];
+
+        if (i > 0)
+            sprintf(fname_str[0], "%s.%d", cur_fname, i-1);
+        else
+            sprintf(fname_str[0], "%s", cur_fname);
+        sprintf(fname_str[1], "%s.%d", cur_fname, i);
+#ifdef WIN32
+        MoveFileEx(fname_str[0], fname_str[1], MOVEFILE_REPLACE_EXISTING);
 #else
-    rename(cur_fname, newname);
+        rename(fname_str[0], fname_str[1]);
 #endif
+    }
 }
 
 
 void yaz_log_init_level(int level)
 {
-    init_mutex();
-    if (l_level < 0) 
-        l_level = default_log_level();
+    internal_log_init();
     if ( (l_level & YLOG_FLUSH) != (level & YLOG_FLUSH) )
     {
         l_level = level;
@@ -200,6 +219,7 @@ void yaz_log_init_level(int level)
     } 
     else
         l_level = level;
+
     if (l_level  & YLOG_LOGLVL)
     {  /* dump the log level bits */
         const char *bittype = "Static ";
@@ -244,7 +264,7 @@ void yaz_log_init_prefix2(const char *prefix)
 
 void yaz_log_init(int level, const char *prefix, const char *fname)
 {
-    init_mutex();
+    internal_log_init();
     yaz_log_init_level(level);
     yaz_log_init_prefix(prefix);
     if (fname && *fname)
@@ -253,10 +273,10 @@ void yaz_log_init(int level, const char *prefix, const char *fname)
 
 void yaz_log_init_max_size(int mx)
 {
-    if (mx <0)
-        l_max_size = l_def_max_size;
-    else
+    if (mx > 0)
         l_max_size = mx;
+    else
+        l_max_size = 0;
 }
 
 void yaz_log_set_handler(void (*func)(int, const char *, void *), void *info)
@@ -282,6 +302,9 @@ static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
     char new_filename[512];
     static char cur_filename[512] = "";
 
+    if (yaz_file_type != use_file)
+        return;
+
     if (l_fname && *l_fname)
     {
         strftime(new_filename, sizeof(new_filename)-1, l_fname, tm);
@@ -292,7 +315,7 @@ static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
         }
     }
 
-    if (l_max_size > 0 && yaz_global_log_file && yaz_file_type == use_file)
+    if (l_max_size > 0 && yaz_global_log_file)
     {
         long flen = ftell(yaz_global_log_file);
         if (flen > l_max_size)
@@ -301,13 +324,25 @@ static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
             force = 1;
         }
     }
-    if (force && yaz_file_type == use_file && *cur_filename)
+    if (force && *cur_filename)
     {
+        FILE *new_file;
+#ifdef WIN32
         yaz_log_close();
-        yaz_global_log_file = fopen(cur_filename, filemode);
-        if (l_level < 0) l_level = default_log_level();
-        if (l_level & YLOG_FLUSH)
-            setvbuf(yaz_global_log_file, 0, _IONBF, 0);
+#endif
+        new_file = fopen(cur_filename, filemode);
+        if (new_file)
+        {
+            yaz_log_close();
+            yaz_global_log_file = new_file;
+            if (l_level & YLOG_FLUSH)
+                setvbuf(yaz_global_log_file, 0, _IONBF, 0);
+        }
+        else
+        {
+            /* disable log rotate */
+            l_max_size = 0;
+        }
     }
 }
 
@@ -354,7 +389,7 @@ static void yaz_strftime(char *dst, size_t sz,
 #ifdef WIN32
         DWORD tid = GetCurrentThreadId();
 #else
-        long tid = 0;
+        pthread_t tid = 0;
 #if YAZ_POSIX_THREADS
         tid = pthread_self();
 #endif
@@ -380,7 +415,7 @@ static void yaz_log_to_file(int level, const char *log_message)
     struct tm *tm;
 #endif
 
-    init_mutex();
+    internal_log_init();
 
     nmem_mutex_enter(log_mutex);
     
@@ -410,16 +445,18 @@ static void yaz_log_to_file(int level, const char *log_message)
                     level &= ~mask_names[i].mask;
                 }
             }
-        
-        if (l_level & YLOG_NOTIME)
-            tbuf[0] = '\0';
-        else
-            yaz_strftime(tbuf, TIMEFORMAT_LEN-1, l_actual_format, tm);
-        tbuf[TIMEFORMAT_LEN-1] = '\0';
-        
-        fprintf(file, "%s %s%s %s%s\n", tbuf, l_prefix, flags, l_prefix2,
+       
+        tbuf[0] = '\0';
+        if (!(l_level & YLOG_NOTIME))
+        {
+            yaz_strftime(tbuf, TIMEFORMAT_LEN-2, l_actual_format, tm);
+            tbuf[TIMEFORMAT_LEN-2] = '\0';
+        }
+        if (tbuf[0])
+            strcat(tbuf, " ");
+        fprintf(file, "%s%s%s %s%s\n", tbuf, l_prefix, flags, l_prefix2,
                 log_message);
-        if (l_level & (YLOG_FLUSH|YLOG_DEBUG) )
+        if (l_level & YLOG_FLUSH)
             fflush(file);
     }
     nmem_mutex_leave(log_mutex);
@@ -432,22 +469,12 @@ void yaz_log(int level, const char *fmt, ...)
     FILE *file;
     int o_level = level;
 
-    if (l_level < 0)
-        l_level = default_log_level();
+    internal_log_init();
     if (!(level & l_level))
         return;
     va_start(ap, fmt);
-#ifdef WIN32
-    _vsnprintf(buf, sizeof(buf)-1, fmt, ap);
-#else
-/* !WIN32 */
-#if HAVE_VSNPRINTF
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-#else
-    vsprintf(buf, fmt, ap);
-#endif
-#endif
-/* WIN32 */
+
+    yaz_vsnprintf(buf, sizeof(buf)-1, fmt, ap);
     if (o_level & YLOG_ERRNO)
     {
         strcat(buf, " [");
@@ -503,7 +530,7 @@ static char *clean_name(const char *name, int len, char *namebuf, int buflen)
 static int define_module_bit(const char *name)
 {
     int i;
-    init_mutex();
+
     nmem_mutex_enter(log_mutex);
     for (i = 0; mask_names[i].name; i++)
         if (0 == strcmp(mask_names[i].name, name))
@@ -511,7 +538,7 @@ static int define_module_bit(const char *name)
             nmem_mutex_leave(log_mutex);
             return mask_names[i].mask;
         }
-    if ( (i>=MAX_MASK_NAMES) || (next_log_bit >= 1<<31 ))
+    if ( (i>=MAX_MASK_NAMES) || (next_log_bit & (1<<31) ))
     {
         nmem_mutex_leave(log_mutex);
         yaz_log(YLOG_WARN, "No more log bits left, not logging '%s'", name);
@@ -532,7 +559,7 @@ int yaz_log_module_level(const char *name)
     int i;
     char clean[255];
     char *n = clean_name(name, strlen(name), clean, sizeof(clean));
-    init_mutex();
+    internal_log_init();
     
     nmem_mutex_enter(log_mutex);
     for (i = 0; mask_names[i].name; i++)
@@ -546,19 +573,21 @@ int yaz_log_module_level(const char *name)
         }
     nmem_mutex_leave(log_mutex);
     yaz_log(YLOG_LOGLVL, "returning NO log bit for '%s' %s", n, 
-                    strcmp(n, name) ? name : "" );
+            strcmp(n, name) ? name : "" );
     return 0;
 }
 
 int yaz_log_mask_str(const char *str)
 {
-    return yaz_log_mask_str_x(str, default_log_level());
+    internal_log_init(); /* since l_level may be affected */
+    return yaz_log_mask_str_x(str, l_level);
 }
 
 int yaz_log_mask_str_x(const char *str, int level)
 {
     const char *p;
 
+    internal_log_init();
     while (*str)
     {
         int negated = 0;