Re-implemented the element name encoding as Adams suggestion: <e tag="value"> when...
[yaz-moved-to-github.git] / src / log.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file log.c
8  * \brief Logging utility
9  */
10
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #ifdef WIN32
16 #include <windows.h>
17 #include <sys/stat.h>
18 #endif
19
20 #if HAVE_SYS_STAT_H
21 #include <sys/stat.h>
22 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <yaz/errno.h>
31 #include <yaz/log.h>
32 #include <yaz/snprintf.h>
33 #include <yaz/xmalloc.h>
34
35 static int l_level = YLOG_DEFAULT_LEVEL;
36
37 enum l_file_type { use_stderr, use_none, use_file };
38 static enum l_file_type yaz_file_type = use_stderr;
39 static FILE *yaz_global_log_file = NULL;
40
41 static void (*start_hook_func)(int, const char *, void *) = NULL;
42 static void *start_hook_info;
43
44 static void (*end_hook_func)(int, const char *, void *) = NULL;
45 static void *end_hook_info;
46
47 static void (*hook_func)(int, const char *, void *) = NULL;
48 static void *hook_info;
49
50 static char l_prefix[512] = "";
51 static char l_prefix2[512] = "";
52 static char l_fname[512] = "";
53
54
55 static char l_old_default_format[] = "%H:%M:%S-%d/%m";
56 static char l_new_default_format[] = "%Y%m%d-%H%M%S";
57 #define TIMEFORMAT_LEN 50
58 static char l_custom_format[TIMEFORMAT_LEN] = "";
59 static char *l_actual_format = l_old_default_format;
60
61 /** l_max_size tells when to rotate the log. The default value is
62     0 which means DISABLED. This is to be preffered if YAZ runs
63     as a server using logrotate etc.
64     A positive size specifies the file size in bytes when a log rotate
65     will occur. Note that in order for this to work YAZ must have
66     permissions to do so.
67  */
68 static int l_max_size = 0;
69
70 #define MAX_MASK_NAMES 35   /* 32 bits plus a few combo names */
71 static struct {
72     int mask;
73     char *name;
74 } mask_names[MAX_MASK_NAMES] =
75 {
76     { YLOG_FATAL,  "fatal"},
77     { YLOG_DEBUG,  "debug"},
78     { YLOG_WARN,   "warn" },
79     { YLOG_LOG,    "log"  },
80     { YLOG_ERRNO,  ""},
81     { YLOG_MALLOC, "malloc"},
82     { YLOG_APP,    "app"  },
83     { YLOG_NOTIME, "notime" },
84     { YLOG_APP2,   "app2" }, 
85     { YLOG_APP3,   "app3" },
86     { YLOG_ALL,    "all"  },
87     { YLOG_FLUSH,  "flush" },
88     { YLOG_LOGLVL, "loglevel" }, 
89     { 0,           "none" },
90     { 0, NULL }
91     /* the rest will be filled in if the user defines dynamic modules*/
92 };  
93
94 static unsigned int next_log_bit = YLOG_LAST_BIT<<1; /* first dynamic bit */
95
96 static void internal_log_init(void)
97 {
98     static int mutex_init_flag = 0; /* not yet initialized */
99     char *env;
100
101     if (mutex_init_flag)
102         return;
103     mutex_init_flag = 1; /* here, 'cause nmem_mutex_create may call yaz_log */
104
105     env = getenv("YAZ_LOG");
106     if (env)
107         l_level = yaz_log_mask_str_x(env, l_level);
108 }
109
110
111 FILE *yaz_log_file(void)
112 {
113     FILE *f = 0;
114     switch(yaz_file_type)
115     {
116         case use_stderr: f = stderr; break;
117         case use_none: f = 0; break;
118         case use_file: f = yaz_global_log_file; break;
119     }
120     return f;
121 }
122
123 void yaz_log_close(void)
124 {
125     if (yaz_file_type == use_file && yaz_global_log_file)
126     {
127         fclose(yaz_global_log_file);
128         yaz_global_log_file = 0;
129     }
130 }
131
132 void yaz_log_init_file(const char *fname)
133 {
134     internal_log_init();
135
136     yaz_log_close();
137     if (fname)
138     {
139         if (*fname == '\0')
140             yaz_file_type = use_stderr; /* empty name; use stderr */
141         else
142             yaz_file_type = use_file;
143         strncpy(l_fname, fname, sizeof(l_fname)-1);
144         l_fname[sizeof(l_fname)-1] = '\0';
145     }
146     else
147     {
148         yaz_file_type = use_none;  /* NULL name; use no file at all */
149         l_fname[0] = '\0'; 
150     }
151     yaz_log_reopen();
152 }
153
154 static void rotate_log(const char *cur_fname)
155 {
156     int i;
157
158 #ifdef WIN32
159     /* windows can't rename a file if it is open */
160     yaz_log_close();
161 #endif
162     for (i = 0; i<9; i++)
163     {
164         char fname_str[FILENAME_MAX];
165         struct stat stat_buf;
166
167         yaz_snprintf(fname_str, sizeof(fname_str), "%s.%d", cur_fname, i);
168         if (stat(fname_str, &stat_buf) != 0)
169             break;
170     }
171     for (; i >= 0; --i)
172     {
173         char fname_str[2][FILENAME_MAX];
174
175         if (i > 0)
176             yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
177                          "%s.%d", cur_fname, i-1);
178         else
179             yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
180                          "%s", cur_fname);
181         yaz_snprintf(fname_str[1], sizeof(fname_str[1]),
182                      "%s.%d", cur_fname, i);
183 #ifdef WIN32
184         MoveFileEx(fname_str[0], fname_str[1], MOVEFILE_REPLACE_EXISTING);
185 #else
186         rename(fname_str[0], fname_str[1]);
187 #endif
188     }
189 }
190
191
192 void yaz_log_init_level(int level)
193 {
194     internal_log_init();
195     if ( (l_level & YLOG_FLUSH) != (level & YLOG_FLUSH) )
196     {
197         l_level = level;
198         yaz_log_reopen(); /* make sure we set buffering right */
199     } 
200     else
201         l_level = level;
202
203     if (l_level  & YLOG_LOGLVL)
204     {  /* dump the log level bits */
205         const char *bittype = "Static ";
206         int i, sz;
207
208         yaz_log(YLOG_LOGLVL, "Setting log level to %d = 0x%08x",
209                 l_level, l_level);
210         /* determine size of mask_names (locked) */
211         for (sz = 0; mask_names[sz].name; sz++)
212             ;
213         /* second pass without lock */
214         for (i = 0; i < sz; i++)
215             if (mask_names[i].mask && *mask_names[i].name)
216                 if (strcmp(mask_names[i].name, "all") != 0)
217                 {
218                     yaz_log(YLOG_LOGLVL, "%s log bit %08x '%s' is %s",
219                             bittype, mask_names[i].mask, mask_names[i].name,
220                             (level & mask_names[i].mask)?  "ON": "off");
221                     if (mask_names[i].mask > YLOG_LAST_BIT)
222                         bittype = "Dynamic";
223                 }
224     }
225 }
226
227 void yaz_log_init_prefix(const char *prefix)
228 {
229     if (prefix && *prefix)
230         yaz_snprintf(l_prefix, sizeof(l_prefix), "%s ", prefix);
231     else
232         *l_prefix = 0;
233 }
234
235 void yaz_log_init_prefix2(const char *prefix)
236 {
237     if (prefix && *prefix)
238         yaz_snprintf(l_prefix2, sizeof(l_prefix2), "%s ", prefix);
239     else
240         *l_prefix2 = 0;
241 }
242
243 void yaz_log_init(int level, const char *prefix, const char *fname)
244 {
245     internal_log_init();
246     yaz_log_init_level(level);
247     yaz_log_init_prefix(prefix);
248     if (fname && *fname)
249         yaz_log_init_file(fname);
250 }
251
252 void yaz_log_init_max_size(int mx)
253 {
254     if (mx > 0)
255         l_max_size = mx;
256     else
257         l_max_size = 0;
258 }
259
260 void yaz_log_set_handler(void (*func)(int, const char *, void *), void *info)
261 {
262     hook_func = func;
263     hook_info = info;
264 }
265
266 void log_event_start(void (*func)(int, const char *, void *), void *info)
267 {
268     start_hook_func = func;
269     start_hook_info = info;
270 }
271
272 void log_event_end(void (*func)(int, const char *, void *), void *info)
273 {
274     end_hook_func = func;
275     end_hook_info = info;
276 }
277
278 static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
279 {
280     char new_filename[512];
281     static char cur_filename[512] = "";
282
283     if (yaz_file_type != use_file)
284         return;
285
286     if (*l_fname)
287     {
288         strftime(new_filename, sizeof(new_filename)-1, l_fname, tm);
289         if (strcmp(new_filename, cur_filename))
290         {
291             strcpy(cur_filename, new_filename);
292             force = 1;
293         }
294     }
295
296     if (l_max_size > 0 && yaz_global_log_file)
297     {
298         long flen = ftell(yaz_global_log_file);
299         if (flen > l_max_size)
300         {
301             rotate_log(cur_filename);
302             force = 1;
303         }
304     }
305     if (force && *cur_filename)
306     {
307         FILE *new_file;
308 #ifdef WIN32
309         yaz_log_close();
310 #endif
311         new_file = fopen(cur_filename, filemode);
312         if (new_file)
313         {
314             yaz_log_close();
315             yaz_global_log_file = new_file;
316             if (l_level & YLOG_FLUSH)
317                 setvbuf(yaz_global_log_file, 0, _IONBF, 0);
318         }
319         else
320         {
321             /* disable log rotate */
322             l_max_size = 0;
323         }
324     }
325 }
326
327 static void yaz_log_do_reopen(const char *filemode)
328 {
329     time_t cur_time = time(0);
330 #if HAVE_LOCALTIME_R
331     struct tm tm0, *tm = &tm0;
332 #else
333     struct tm *tm;
334 #endif
335
336 #if HAVE_LOCALTIME_R
337     localtime_r(&cur_time, tm);
338 #else
339     tm = localtime(&cur_time);
340 #endif
341     yaz_log_open_check(tm, 1, filemode);
342 }
343
344
345 void yaz_log_reopen()
346 {
347     yaz_log_do_reopen("a");
348 }
349
350 void yaz_log_trunc()
351 {
352     yaz_log_do_reopen("w");
353 }
354
355 static void yaz_strftime(char *dst, size_t sz,
356                          const char *fmt, const struct tm *tm)
357 {
358     strftime(dst, sz, fmt, tm);
359 }
360                             
361 static void yaz_log_to_file(int level, const char *log_message)
362 {
363     FILE *file;
364     time_t ti = time(0);
365 #if HAVE_LOCALTIME_R
366     struct tm tm0, *tm = &tm0;
367 #else
368     struct tm *tm;
369 #endif
370
371     internal_log_init();
372
373 #if HAVE_LOCALTIME_R
374     localtime_r(&ti, tm);
375 #else
376     tm = localtime(&ti);
377 #endif
378     
379     yaz_log_open_check(tm, 0, "a");  
380     file = yaz_log_file(); /* file may change in yaz_log_open_check */
381
382     if (file)
383     {
384         char tbuf[TIMEFORMAT_LEN];
385         char flags[1024];
386         int i;
387         
388         *flags = '\0';
389         for (i = 0; level && mask_names[i].name; i++)
390             if ( mask_names[i].mask & level)
391             {
392                 if (*mask_names[i].name && mask_names[i].mask && 
393                     mask_names[i].mask != YLOG_ALL)
394                 {
395                     if (strlen(flags) + strlen(mask_names[i].name) 
396                                              <   sizeof(flags) - 4)
397                     {
398                         strcat(flags, "[");
399                         strcat(flags, mask_names[i].name);
400                         strcat(flags, "]");
401                     }
402                     level &= ~mask_names[i].mask;
403                 }
404             }
405        
406         tbuf[0] = '\0';
407         if (!(l_level & YLOG_NOTIME))
408         {
409             yaz_strftime(tbuf, TIMEFORMAT_LEN-2, l_actual_format, tm);
410             tbuf[TIMEFORMAT_LEN-2] = '\0';
411         }
412         if (tbuf[0])
413             strcat(tbuf, " ");
414         fprintf(file, "%s%s%s %s%s\n", tbuf, l_prefix, flags, l_prefix2,
415                 log_message);
416         if (l_level & YLOG_FLUSH)
417             fflush(file);
418     }
419 }
420
421 void yaz_log(int level, const char *fmt, ...)
422 {
423     va_list ap;
424     char buf[4096];
425     FILE *file;
426     int o_level = level;
427
428     internal_log_init();
429     if (!(level & l_level))
430         return;
431     va_start(ap, fmt);
432
433     /* 30 is enough for our 'rest of output' message */
434     yaz_vsnprintf(buf, sizeof(buf)-30, fmt, ap);
435     if (strlen(buf) >= sizeof(buf)-31)
436         strcat(buf, " [rest of output omitted]");
437
438     if (o_level & YLOG_ERRNO)
439     {
440         size_t remain = sizeof(buf) - strlen(buf);
441         if (remain > 100) /* reasonable minimum space for error */
442         {
443             strcat(buf, " [");
444             yaz_strerror(buf+strlen(buf), remain-5); /* 5 due to extra [] */
445             strcat(buf, "]");
446         }
447     }
448     va_end (ap);
449     if (start_hook_func)
450         (*start_hook_func)(o_level, buf, start_hook_info);
451     if (hook_func)
452         (*hook_func)(o_level, buf, hook_info);
453     file = yaz_log_file();
454     if (file)
455         yaz_log_to_file(level, buf);
456     if (end_hook_func)
457         (*end_hook_func)(o_level, buf, end_hook_info);
458 }
459
460 void yaz_log_time_format(const char *fmt)
461 {
462     if ( !fmt || !*fmt) 
463     { /* no format, default to new */
464         l_actual_format = l_new_default_format;
465         return; 
466     }
467     if (0==strcmp(fmt, "old"))
468     { /* force the old format */
469         l_actual_format = l_old_default_format;
470         return; 
471     }
472     /* else use custom format */
473     strncpy(l_custom_format, fmt, TIMEFORMAT_LEN-1);
474     l_custom_format[TIMEFORMAT_LEN-1] = '\0';
475     l_actual_format = l_custom_format;
476 }
477
478 /** cleans a loglevel name from leading paths and suffixes */
479 static char *clean_name(const char *name, size_t len, char *namebuf, size_t buflen)
480 {
481     char *p = namebuf;
482     char *start = namebuf;
483     if (buflen <= len)
484         len = buflen-1; 
485     strncpy(namebuf, name, len);
486     namebuf[len] = '\0';
487     while ((p = strchr(start, '/')))
488         start = p+1;
489     if ((p = strrchr(start, '.')))
490         *p = '\0';
491     return start;
492 }
493
494 static int define_module_bit(const char *name)
495 {
496     size_t i;
497
498     for (i = 0; mask_names[i].name; i++)
499         if (0 == strcmp(mask_names[i].name, name))
500         {
501             return mask_names[i].mask;
502         }
503     if ( (i>=MAX_MASK_NAMES) || (next_log_bit & (1U<<31) ))
504     {
505         yaz_log(YLOG_WARN, "No more log bits left, not logging '%s'", name);
506         return 0;
507     }
508     mask_names[i].mask = (int) next_log_bit; /* next_log_bit can hold int */
509     next_log_bit = next_log_bit<<1;
510     mask_names[i].name = (char *) malloc(strlen(name)+1);
511     strcpy(mask_names[i].name, name);
512     mask_names[i+1].name = NULL;
513     mask_names[i+1].mask = 0;
514     return mask_names[i].mask;
515 }
516
517 int yaz_log_module_level(const char *name)
518 {
519     int i;
520     char clean[255];
521     char *n = clean_name(name, strlen(name), clean, sizeof(clean));
522     internal_log_init();
523     
524     for (i = 0; mask_names[i].name; i++)
525         if (0==strcmp(n, mask_names[i].name))
526         {
527             yaz_log(YLOG_LOGLVL, "returning log bit 0x%x for '%s' %s",
528                     mask_names[i].mask, n, 
529                     strcmp(n,name) ? name : "");
530             return mask_names[i].mask;
531         }
532     yaz_log(YLOG_LOGLVL, "returning NO log bit for '%s' %s", n, 
533             strcmp(n, name) ? name : "" );
534     return 0;
535 }
536
537 int yaz_log_mask_str(const char *str)
538 {
539     internal_log_init(); /* since l_level may be affected */
540     return yaz_log_mask_str_x(str, l_level);
541 }
542
543 int yaz_log_mask_str_x(const char *str, int level)
544 {
545     const char *p;
546
547     internal_log_init();
548     while (*str)
549     {
550         int negated = 0;
551         for (p = str; *p && *p != ','; p++)
552             ;
553         if (*str=='-')
554         {
555             negated = 1;
556             str++;
557         }
558         if (isdigit(*(unsigned char *) str))
559         {
560             level = atoi(str);
561         }
562         else 
563         {
564             char clean[509];
565             char *n = clean_name(str, (size_t) (p - str), clean, sizeof(clean));
566             int mask = define_module_bit(n);
567             if (!mask)
568                 level = 0;  /* 'none' clears them all */
569             else if (negated)
570                 level &= ~mask;
571             else
572                 level |= mask;
573         }
574         if (*p == ',')
575             p++;
576         str = p;
577     }
578     return level;
579 }
580 /*
581  * Local variables:
582  * c-basic-offset: 4
583  * c-file-style: "Stroustrup"
584  * indent-tabs-mode: nil
585  * End:
586  * vim: shiftwidth=4 tabstop=8 expandtab
587  */
588