Add lock/unlock for YAZ log writes YAZ-843
[yaz-moved-to-github.git] / src / log.c
index dec6a2a..8bc5ad0 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,5 +1,5 @@
 /* This file is part of the YAZ toolkit.
- * Copyright (C) 1995-2012 Index Data
+ * Copyright (C) Index Data
  * See the file LICENSE for details.
  */
 
 #include <yaz/errno.h>
 #include <yaz/thread_id.h>
 #include <yaz/log.h>
+#include <yaz/mutex.h>
 #include <yaz/snprintf.h>
 #include <yaz/xmalloc.h>
+#if YAZ_POSIX_THREADS
+#include <pthread.h>
+#endif
 
 static int l_level = YLOG_DEFAULT_LEVEL;
 
@@ -99,21 +103,32 @@ static struct {
 
 static unsigned int next_log_bit = YLOG_LAST_BIT<<1; /* first dynamic bit */
 
-static void internal_log_init(void)
+static YAZ_MUTEX log_mutex = 0;
+
+void yaz_log_lock(void)
 {
-    static int mutex_init_flag = 0; /* not yet initialized */
-    char *env;
+    yaz_mutex_enter(log_mutex);
+}
 
-    if (mutex_init_flag)
-        return;
-    mutex_init_flag = 1; /* here, 'cause nmem_mutex_create may call yaz_log */
+void yaz_log_unlock(void)
+{
+    yaz_mutex_leave(log_mutex);
+}
 
+void yaz_log_init_globals(void)
+{
+    char *env;
+
+    if (log_mutex == 0)
+        yaz_mutex_create(&log_mutex);
+#if YAZ_POSIX_THREADS
+    pthread_atfork(yaz_log_lock, yaz_log_unlock, yaz_log_unlock);
+#endif
     env = getenv("YAZ_LOG");
     if (env)
         l_level = yaz_log_mask_str_x(env, l_level);
 }
 
-
 FILE *yaz_log_file(void)
 {
     FILE *f = 0;
@@ -137,7 +152,7 @@ void yaz_log_close(void)
 
 void yaz_log_init_file(const char *fname)
 {
-    internal_log_init();
+    yaz_init_globals();
 
     yaz_log_close();
     if (fname)
@@ -197,7 +212,7 @@ static void rotate_log(const char *cur_fname)
 
 void yaz_log_init_level(int level)
 {
-    internal_log_init();
+    yaz_init_globals();
     if ( (l_level & YLOG_FLUSH) != (level & YLOG_FLUSH) )
     {
         l_level = level;
@@ -250,7 +265,7 @@ void yaz_log_init_prefix2(const char *prefix)
 
 void yaz_log_init(int level, const char *prefix, const char *fname)
 {
-    internal_log_init();
+    yaz_init_globals();
     yaz_log_init_level(level);
     yaz_log_init_prefix(prefix);
     if (fname && *fname)
@@ -317,13 +332,14 @@ static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
 #ifdef WIN32
         yaz_log_close();
 #endif
-        new_file = fopen(cur_filename, filemode);
+        if (!strncmp(cur_filename, "fd=", 3))
+            new_file = fdopen(atoi(cur_filename + 3), filemode);
+        else
+            new_file = fopen(cur_filename, filemode);
         if (new_file)
         {
             yaz_log_close();
             yaz_log_info.log_file = new_file;
-            if (l_level & YLOG_FLUSH)
-                setvbuf(yaz_log_info.log_file, 0, _IONBF, 0);
         }
         else
         {
@@ -342,12 +358,14 @@ static void yaz_log_do_reopen(const char *filemode)
     struct tm *tm;
 #endif
 
+    yaz_log_lock();
 #if HAVE_LOCALTIME_R
     localtime_r(&cur_time, tm);
 #else
     tm = localtime(&cur_time);
 #endif
     yaz_log_open_check(tm, 1, filemode);
+    yaz_log_unlock();
 }
 
 
@@ -367,7 +385,8 @@ static void yaz_strftime(char *dst, size_t sz,
     strftime(dst, sz, fmt, tm);
 }
 
-static void yaz_log_to_file(int level, const char *log_message)
+static void yaz_log_to_file(int level, const char *fmt, va_list ap,
+                            const char *error_cp)
 {
     FILE *file;
     time_t ti = time(0);
@@ -377,8 +396,7 @@ static void yaz_log_to_file(int level, const char *log_message)
     struct tm *tm;
 #endif
 
-    internal_log_init();
-
+    yaz_log_lock();
 #if HAVE_LOCALTIME_R
     localtime_r(&ti, tm);
 #else
@@ -430,51 +448,58 @@ static void yaz_log_to_file(int level, const char *log_message)
                 strcat(tid, " ");
         }
 
-        fprintf(file, "%s%s%s%s %s%s\n", tbuf, yaz_log_info.l_prefix,
-                tid, flags, yaz_log_info.l_prefix2,
-                log_message);
+        fprintf(file, "%s%s%s%s %s", tbuf, yaz_log_info.l_prefix,
+                tid, flags, yaz_log_info.l_prefix2);
+        vfprintf(file, fmt, ap);
+        if (error_cp)
+            fprintf(file, " [%s]", error_cp);
+        fputs("\n", file);
         if (l_level & YLOG_FLUSH)
             fflush(file);
     }
+    yaz_log_unlock();
 }
 
 void yaz_log(int level, const char *fmt, ...)
 {
     va_list ap;
-    char buf[4096];
     FILE *file;
     int o_level = level;
+    char *error_cp = 0, error_buf[128];
 
-    internal_log_init();
+    if (o_level & YLOG_ERRNO)
+    {
+        yaz_strerror(error_buf, sizeof(error_buf));
+        error_cp = error_buf;
+    }
+    yaz_init_globals();
     if (!(level & l_level))
         return;
     va_start(ap, fmt);
 
-    /* 30 is enough for our 'rest of output' message */
-    yaz_vsnprintf(buf, sizeof(buf)-30, fmt, ap);
-    if (strlen(buf) >= sizeof(buf)-31)
-        strcat(buf, " [rest of output omitted]");
-
-    if (o_level & YLOG_ERRNO)
+    file = yaz_log_file();
+    if (start_hook_func || hook_func || end_hook_func)
     {
-        size_t remain = sizeof(buf) - strlen(buf);
-        if (remain > 100) /* reasonable minimum space for error */
-        {
-            strcat(buf, " [");
-            yaz_strerror(buf+strlen(buf), remain-5); /* 5 due to extra [] */
-            strcat(buf, "]");
-        }
+        char buf[1024];
+        /* 30 is enough for our 'rest of output' message */
+        yaz_vsnprintf(buf, sizeof(buf)-30, fmt, ap);
+        if (strlen(buf) >= sizeof(buf)-31)
+            strcat(buf, " [rest of output omitted]");
+        if (start_hook_func)
+            (*start_hook_func)(o_level, buf, start_hook_info);
+        if (hook_func)
+            (*hook_func)(o_level, buf, hook_info);
+        if (file)
+            yaz_log_to_file(level, fmt, ap, error_cp);
+        if (end_hook_func)
+            (*end_hook_func)(o_level, buf, end_hook_info);
     }
-    va_end (ap);
-    if (start_hook_func)
-        (*start_hook_func)(o_level, buf, start_hook_info);
-    if (hook_func)
-        (*hook_func)(o_level, buf, hook_info);
-    file = yaz_log_file();
-    if (file)
-        yaz_log_to_file(level, buf);
-    if (end_hook_func)
-        (*end_hook_func)(o_level, buf, end_hook_info);
+    else
+    {
+        if (file)
+            yaz_log_to_file(level, fmt, ap, error_cp);
+    }
+    va_end(ap);
 }
 
 void yaz_log_time_format(const char *fmt)
@@ -539,7 +564,7 @@ int yaz_log_module_level(const char *name)
     int i;
     char clean[255];
     char *n = clean_name(name, strlen(name), clean, sizeof(clean));
-    internal_log_init();
+    yaz_init_globals();
 
     for (i = 0; mask_names[i].name; i++)
         if (0==strcmp(n, mask_names[i].name))
@@ -556,15 +581,16 @@ int yaz_log_module_level(const char *name)
 
 int yaz_log_mask_str(const char *str)
 {
-    internal_log_init(); /* since l_level may be affected */
+    yaz_init_globals(); /* since l_level may be affected */
     return yaz_log_mask_str_x(str, l_level);
 }
 
+/* this function is called by yaz_log_init_globals & yaz_init_globals
+   and, thus, may not call any of them indirectly */
 int yaz_log_mask_str_x(const char *str, int level)
 {
     const char *p;
 
-    internal_log_init();
     while (*str)
     {
         int negated = 0;