YAZ 5.35.1
log.c
Go to the documentation of this file.
1/* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
4 */
5
11#if HAVE_CONFIG_H
12#include <config.h>
13#endif
14
15#include <yaz/yconfig.h>
16
17#ifdef WIN32
18#include <windows.h>
19#include <sys/stat.h>
20#endif
21
22#if HAVE_SYS_STAT_H
23#include <sys/stat.h>
24#endif
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdarg.h>
29#include <errno.h>
30#include <time.h>
31#include <yaz/yaz-iconv.h>
32#include <yaz/errno.h>
33#include <yaz/thread_id.h>
34#include <yaz/log.h>
35#include <yaz/mutex.h>
36#include <yaz/snprintf.h>
37#include <yaz/xmalloc.h>
38#if YAZ_POSIX_THREADS
39#include <pthread.h>
40#endif
41
43
45
46struct {
48 FILE *log_file;
49 char l_prefix[512];
50 char l_prefix2[512];
51 char l_fname[512];
52} yaz_log_info = {
53 use_stderr, 0, "", "", ""
54};
55
56static void (*start_hook_func)(int, const char *, void *) = NULL;
57static void *start_hook_info;
58
59static void (*end_hook_func)(int, const char *, void *) = NULL;
60static void *end_hook_info;
61
62static void (*hook_func)(int, const char *, void *) = NULL;
63static void *hook_info;
64
65static char l_old_default_format[] = "%H:%M:%S-%d/%m";
66static char l_new_default_format[] = "%Y%m%d-%H%M%S";
67#define TIMEFORMAT_LEN 50
68#define TID_LEN 30
71
79static int l_max_size = 0;
80
81#define MAX_MASK_NAMES 35 /* 32 bits plus a few combo names */
82static struct {
83 int mask;
84 char *name;
86{
87 { YLOG_FATAL, "fatal"},
88 { YLOG_DEBUG, "debug"},
89 { YLOG_WARN, "warn" },
90 { YLOG_LOG, "log" },
91 { YLOG_ERRNO, ""},
92 { YLOG_MALLOC, "malloc"},
93 { YLOG_TID, "tid" },
94 { YLOG_APP, "app" },
95 { YLOG_NOTIME, "notime" },
96 { YLOG_APP2, "app2" },
97 { YLOG_APP3, "app3" },
98 { YLOG_ALL, "all" },
99 { YLOG_FLUSH, "flush" },
100 { YLOG_LOGLVL, "loglevel" },
101 { 0, "none" },
102 { 0, NULL }
103 /* the rest will be filled in if the user defines dynamic modules*/
105
106static unsigned int next_log_bit = YLOG_LAST_BIT<<1; /* first dynamic bit */
107
108static int yaz_log_reopen_flag = 0;
109
111
112static void yaz_log_open(void);
113
114void yaz_log_lock(void)
115{
117}
118
120{
122}
123
125{
126 char *env;
127
128 if (log_mutex == 0)
130#if YAZ_POSIX_THREADS
132#endif
133 env = getenv("YAZ_LOG");
134 if (env)
136}
137
138FILE *yaz_log_file(void)
139{
140 FILE *f = 0;
141 switch (yaz_log_info.type)
142 {
143 case use_stderr: f = stderr; break;
144 case use_none: f = 0; break;
145 case use_file: f = yaz_log_info.log_file; break;
146 }
147 return f;
148}
149
151{
152 if (yaz_log_info.type == use_file && yaz_log_info.log_file)
153 {
154 fclose(yaz_log_info.log_file);
155 yaz_log_info.log_file = 0;
156 }
157}
158
160{
161 if (log_mutex)
162 {
165 }
166}
167
168void yaz_log_init_file(const char *fname)
169{
171
173 if (fname)
174 {
175 if (*fname == '\0')
176 yaz_log_info.type = use_stderr; /* empty name; use stderr */
177 else
178 yaz_log_info.type = use_file;
179 strncpy(yaz_log_info.l_fname, fname, sizeof(yaz_log_info.l_fname)-1);
180 yaz_log_info.l_fname[sizeof(yaz_log_info.l_fname)-1] = '\0';
181 }
182 else
183 {
184 yaz_log_info.type = use_none; /* NULL name; use no file at all */
185 yaz_log_info.l_fname[0] = '\0';
186 }
187 yaz_log_open();
188}
189
190static void rotate_log(const char *cur_fname)
191{
192 int i;
193
194#ifdef WIN32
195 /* windows can't rename a file if it is open */
197#endif
198 for (i = 0; i<9; i++)
199 {
200 char fname_str[FILENAME_MAX];
201 struct stat stat_buf;
202
203 yaz_snprintf(fname_str, sizeof(fname_str), "%s.%d", cur_fname, i);
204 if (stat(fname_str, &stat_buf) != 0)
205 break;
206 }
207 for (; i >= 0; --i)
208 {
209 char fname_str[2][FILENAME_MAX];
210
211 if (i > 0)
212 yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
213 "%s.%d", cur_fname, i-1);
214 else
215 yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
216 "%s", cur_fname);
217 yaz_snprintf(fname_str[1], sizeof(fname_str[1]),
218 "%s.%d", cur_fname, i);
219#ifdef WIN32
220 MoveFileEx(fname_str[0], fname_str[1], MOVEFILE_REPLACE_EXISTING);
221#else
222 rename(fname_str[0], fname_str[1]);
223#endif
224 }
225}
226
227
228void yaz_log_init_level(int level)
229{
231 if ( (l_level & YLOG_FLUSH) != (level & YLOG_FLUSH) )
232 {
233 l_level = level;
234 yaz_log_open(); /* make sure we set buffering right */
235 }
236 else
237 l_level = level;
238
239 if (l_level & YLOG_LOGLVL)
240 { /* dump the log level bits */
241 const char *bittype = "Static ";
242 int i, sz;
243
244 yaz_log(YLOG_LOGLVL, "Setting log level to %d = 0x%08x",
246 /* determine size of mask_names (locked) */
247 for (sz = 0; mask_names[sz].name; sz++)
248 ;
249 /* second pass without lock */
250 for (i = 0; i < sz; i++)
251 if (mask_names[i].mask && *mask_names[i].name)
252 if (strcmp(mask_names[i].name, "all") != 0)
253 {
254 yaz_log(YLOG_LOGLVL, "%s log bit %08x '%s' is %s",
255 bittype, mask_names[i].mask, mask_names[i].name,
256 (level & mask_names[i].mask)? "ON": "off");
258 bittype = "Dynamic";
259 }
260 }
261}
262
263void yaz_log_init_prefix(const char *prefix)
264{
265 if (prefix && *prefix)
266 yaz_snprintf(yaz_log_info.l_prefix,
267 sizeof(yaz_log_info.l_prefix), "%s ", prefix);
268 else
269 *yaz_log_info.l_prefix = 0;
270}
271
272void yaz_log_init_prefix2(const char *prefix)
273{
274 if (prefix && *prefix)
275 yaz_snprintf(yaz_log_info.l_prefix2,
276 sizeof(yaz_log_info.l_prefix2), "%s ", prefix);
277 else
278 *yaz_log_info.l_prefix2 = 0;
279}
280
281void yaz_log_init(int level, const char *prefix, const char *fname)
282{
284 yaz_log_init_level(level);
285 yaz_log_init_prefix(prefix);
286 if (fname && *fname)
287 yaz_log_init_file(fname);
288}
289
291{
292 if (mx > 0)
293 l_max_size = mx;
294 else
295 l_max_size = 0;
296}
297
298void yaz_log_set_handler(void (*func)(int, const char *, void *), void *info)
299{
300 hook_func = func;
301 hook_info = info;
302}
303
304void log_event_start(void (*func)(int, const char *, void *), void *info)
305{
306 start_hook_func = func;
307 start_hook_info = info;
308}
309
310void log_event_end(void (*func)(int, const char *, void *), void *info)
311{
312 end_hook_func = func;
313 end_hook_info = info;
314}
315
316static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
317{
318 char new_filename[512];
319 static char cur_filename[512] = "";
320
321 if (yaz_log_info.type != use_file)
322 return;
323
325 {
326 force = 1;
328 }
329 if (*yaz_log_info.l_fname)
330 {
331 strftime(new_filename, sizeof(new_filename)-1, yaz_log_info.l_fname,
332 tm);
333 if (strcmp(new_filename, cur_filename))
334 {
335 strcpy(cur_filename, new_filename);
336 force = 1;
337 }
338 }
339
340 if (l_max_size > 0 && yaz_log_info.log_file)
341 {
342 long flen = ftell(yaz_log_info.log_file);
343 if (flen > l_max_size)
344 {
345 rotate_log(cur_filename);
346 force = 1;
347 }
348 }
349 if (force && *cur_filename)
350 {
351 FILE *new_file;
352#ifdef WIN32
354#endif
355 if (!strncmp(cur_filename, "fd=", 3))
356 new_file = fdopen(atoi(cur_filename + 3), filemode);
357 else
358 new_file = fopen(cur_filename, filemode);
359 if (new_file)
360 {
362 yaz_log_info.log_file = new_file;
363 }
364 else
365 {
366 /* disable log rotate */
367 l_max_size = 0;
368 }
369 }
370}
371
372static void yaz_log_do_reopen(const char *filemode)
373{
374 time_t cur_time = time(0);
375#if HAVE_LOCALTIME_R
376 struct tm tm0, *tm = &tm0;
377#else
378 struct tm *tm;
379#endif
380
381 yaz_log_lock();
382#if HAVE_LOCALTIME_R
383 localtime_r(&cur_time, tm);
384#else
385 tm = localtime(&cur_time);
386#endif
387 yaz_log_open_check(tm, 1, filemode);
389}
390
392{
394}
395
396static void yaz_log_open()
397{
399}
400
402{
404}
405
406static void yaz_strftime(char *dst, size_t sz,
407 const char *fmt, const struct tm *tm)
408{
409 strftime(dst, sz, fmt, tm);
410}
411
412static void yaz_log_to_file(int level, const char *fmt, va_list ap,
413 const char *error_cp)
414{
415 FILE *file;
416 time_t ti = time(0);
417#if HAVE_LOCALTIME_R
418 struct tm tm0, *tm = &tm0;
419#else
420 struct tm *tm;
421#endif
422
423 yaz_log_lock();
424#if HAVE_LOCALTIME_R
425 localtime_r(&ti, tm);
426#else
427 tm = localtime(&ti);
428#endif
429
430 yaz_log_open_check(tm, 0, "a");
431 file = yaz_log_file(); /* file may change in yaz_log_open_check */
432
433 if (file)
434 {
435 char tbuf[TIMEFORMAT_LEN];
436 char tid[TID_LEN];
437 char flags[1024];
438 int i;
439
440 *flags = '\0';
441 for (i = 0; level && mask_names[i].name; i++)
442 if ( mask_names[i].mask & level)
443 {
444 if (*mask_names[i].name && mask_names[i].mask &&
446 {
447 if (strlen(flags) + strlen(mask_names[i].name)
448 < sizeof(flags) - 4)
449 {
450 strcat(flags, "[");
451 strcat(flags, mask_names[i].name);
452 strcat(flags, "]");
453 }
454 level &= ~mask_names[i].mask;
455 }
456 }
457
458 tbuf[0] = '\0';
459 if (!(l_level & YLOG_NOTIME))
460 {
462 tbuf[TIMEFORMAT_LEN-2] = '\0';
463 }
464 if (tbuf[0])
465 strcat(tbuf, " ");
466 tid[0] = '\0';
467
468 if (l_level & YLOG_TID)
469 {
470 yaz_thread_id_cstr(tid, sizeof(tid)-1);
471 if (tid[0])
472 strcat(tid, " ");
473 }
474
475 fprintf(file, "%s%s%s%s %s", tbuf, yaz_log_info.l_prefix,
476 tid, flags, yaz_log_info.l_prefix2);
477 vfprintf(file, fmt, ap);
478 if (error_cp)
479 fprintf(file, " [%s]", error_cp);
480 fputs("\n", file);
481 if (l_level & YLOG_FLUSH)
482 fflush(file);
483 }
485}
486
487void yaz_log(int level, const char *fmt, ...)
488{
489 va_list ap;
490 FILE *file;
491 int o_level = level;
492 char *error_cp = 0, error_buf[128];
493
494 if (o_level & YLOG_ERRNO)
495 {
496 yaz_strerror(error_buf, sizeof(error_buf));
497 error_cp = error_buf;
498 }
500 if (!(level & l_level))
501 return;
502 va_start(ap, fmt);
503
504 file = yaz_log_file();
506 {
507 char buf[1024];
508 /* 30 is enough for our 'rest of output' message */
509 yaz_vsnprintf(buf, sizeof(buf)-30, fmt, ap);
510 if (strlen(buf) >= sizeof(buf)-31)
511 strcat(buf, " [rest of output omitted]");
512 if (start_hook_func)
513 (*start_hook_func)(o_level, buf, start_hook_info);
514 if (hook_func)
515 (*hook_func)(o_level, buf, hook_info);
516 if (file)
517 yaz_log_to_file(level, fmt, ap, error_cp);
518 if (end_hook_func)
519 (*end_hook_func)(o_level, buf, end_hook_info);
520 }
521 else
522 {
523 if (file)
524 yaz_log_to_file(level, fmt, ap, error_cp);
525 }
526 va_end(ap);
527}
528
529void yaz_log_time_format(const char *fmt)
530{
531 if ( !fmt || !*fmt)
532 { /* no format, default to new */
534 return;
535 }
536 if (0==strcmp(fmt, "old"))
537 { /* force the old format */
539 return;
540 }
541 /* else use custom format */
542 strncpy(l_custom_format, fmt, TIMEFORMAT_LEN-1);
545}
546
548static char *clean_name(const char *name, size_t len, char *namebuf, size_t buflen)
549{
550 char *p = namebuf;
551 char *start = namebuf;
552 if (buflen <= len)
553 len = buflen-1;
554 strncpy(namebuf, name, len);
555 namebuf[len] = '\0';
556 while ((p = strchr(start, '/')))
557 start = p+1;
558 if ((p = strrchr(start, '.')))
559 *p = '\0';
560 return start;
561}
562
563static int define_module_bit(const char *name)
564{
565 size_t i;
566
567 for (i = 0; mask_names[i].name; i++)
568 if (0 == strcmp(mask_names[i].name, name))
569 {
570 return mask_names[i].mask;
571 }
572 if ( (i>=MAX_MASK_NAMES) || (next_log_bit & (1U<<31) ))
573 {
574 yaz_log(YLOG_WARN, "No more log bits left, not logging '%s'", name);
575 return 0;
576 }
577 mask_names[i].mask = (int) next_log_bit; /* next_log_bit can hold int */
579 mask_names[i].name = (char *) malloc(strlen(name)+1);
580 strcpy(mask_names[i].name, name);
581 mask_names[i+1].name = NULL;
582 mask_names[i+1].mask = 0;
583 return mask_names[i].mask;
584}
585
587{
588 int i;
589 char clean[255];
590 char *n = clean_name(name, strlen(name), clean, sizeof(clean));
592
593 for (i = 0; mask_names[i].name; i++)
594 if (0==strcmp(n, mask_names[i].name))
595 {
596 yaz_log(YLOG_LOGLVL, "returning log bit 0x%x for '%s' %s",
597 mask_names[i].mask, n,
598 strcmp(n,name) ? name : "");
599 return mask_names[i].mask;
600 }
601 yaz_log(YLOG_LOGLVL, "returning NO log bit for '%s' %s", n,
602 strcmp(n, name) ? name : "" );
603 return 0;
604}
605
606int yaz_log_mask_str(const char *str)
607{
608 yaz_init_globals(); /* since l_level may be affected */
609 return yaz_log_mask_str_x(str, l_level);
610}
611
612/* this function is called by yaz_log_init_globals & yaz_init_globals
613 and, thus, may not call any of them indirectly */
614int yaz_log_mask_str_x(const char *str, int level)
615{
616 const char *p;
617
618 while (*str)
619 {
620 int negated = 0;
621 for (p = str; *p && *p != ','; p++)
622 ;
623 if (*str=='-')
624 {
625 negated = 1;
626 str++;
627 }
628 if (yaz_isdigit(*str))
629 {
630 level = atoi(str);
631 }
632 else
633 {
634 char clean[509];
635 char *n = clean_name(str, (size_t) (p - str), clean, sizeof(clean));
636 int mask = define_module_bit(n);
637 if (!mask)
638 level = 0; /* 'none' clears them all */
639 else if (negated)
640 level &= ~mask;
641 else
642 level |= mask;
643 }
644 if (*p == ',')
645 p++;
646 str = p;
647 }
648 return level;
649}
650/*
651 * Local variables:
652 * c-basic-offset: 4
653 * c-file-style: "Stroustrup"
654 * indent-tabs-mode: nil
655 * End:
656 * vim: shiftwidth=4 tabstop=8 expandtab
657 */
658
void * malloc(YYSIZE_T)
void yaz_strerror(char *buf, size_t bufsz)
returns system error description string
Definition errno.c:41
Header for errno utilities.
void yaz_init_globals(void)
void yaz_log_set_handler(void(*func)(int, const char *, void *), void *info)
sets custom log handler
Definition log.c:298
static void yaz_log_to_file(int level, const char *fmt, va_list ap, const char *error_cp)
Definition log.c:412
static int l_level
Definition log.c:42
static void(* end_hook_func)(int, const char *, void *)
Definition log.c:59
void yaz_log_init_prefix(const char *prefix)
sets log message prefix
Definition log.c:263
void yaz_log_init_globals(void)
Definition log.c:124
#define MAX_MASK_NAMES
Definition log.c:81
static unsigned int next_log_bit
Definition log.c:106
static struct @4 mask_names[MAX_MASK_NAMES]
char l_prefix2[512]
Definition log.c:50
int yaz_log_mask_str(const char *str)
converts log level string to log level (integer)
Definition log.c:606
void yaz_log_init_max_size(int mx)
sets limit in bytes for size for log file
Definition log.c:290
void log_event_start(void(*func)(int, const char *, void *), void *info)
Definition log.c:304
void yaz_log_init_prefix2(const char *prefix)
sets second log message prefix
Definition log.c:272
static void(* start_hook_func)(int, const char *, void *)
Definition log.c:56
void yaz_log_init_level(int level)
sets log level
Definition log.c:228
char * name
Definition log.c:84
static void yaz_strftime(char *dst, size_t sz, const char *fmt, const struct tm *tm)
Definition log.c:406
static void * end_hook_info
Definition log.c:60
static void * hook_info
Definition log.c:63
static char l_custom_format[TIMEFORMAT_LEN]
Definition log.c:69
static int l_max_size
Definition log.c:79
void yaz_log_reopen()
reopen current log file (unless disabled or stderr)
Definition log.c:391
void yaz_log_init(int level, const char *prefix, const char *fname)
sets level, prefix and filename for logging
Definition log.c:281
static char * l_actual_format
Definition log.c:70
static YAZ_MUTEX log_mutex
Definition log.c:110
void yaz_log_deinit_globals(void)
Definition log.c:159
static int yaz_log_reopen_flag
Definition log.c:108
enum l_file_type type
Definition log.c:47
char l_prefix[512]
Definition log.c:49
l_file_type
Definition log.c:44
@ use_file
Definition log.c:44
@ use_stderr
Definition log.c:44
@ use_none
Definition log.c:44
static char l_old_default_format[]
Definition log.c:65
static int define_module_bit(const char *name)
Definition log.c:563
static void(* hook_func)(int, const char *, void *)
Definition log.c:62
struct @3 yaz_log_info
static void * start_hook_info
Definition log.c:57
int mask
Definition log.c:83
FILE * log_file
Definition log.c:48
#define TID_LEN
Definition log.c:68
static char * clean_name(const char *name, size_t len, char *namebuf, size_t buflen)
Definition log.c:548
static void yaz_log_do_reopen(const char *filemode)
Definition log.c:372
void yaz_log_lock(void)
Lock for YAZ log writes.
Definition log.c:114
void yaz_log_time_format(const char *fmt)
sets time format for log mesages
Definition log.c:529
static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
Definition log.c:316
void yaz_log(int level, const char *fmt,...)
Writes log message.
Definition log.c:487
static void rotate_log(const char *cur_fname)
Definition log.c:190
void yaz_log_close(void)
Definition log.c:150
void yaz_log_init_file(const char *fname)
sets log file
Definition log.c:168
void yaz_log_trunc()
Truncate the log file.
Definition log.c:401
char l_fname[512]
Definition log.c:51
void yaz_log_unlock(void)
Unlock for YAZ log writes.
Definition log.c:119
int yaz_log_mask_str_x(const char *str, int level)
converts log level string to log level with "start" level
Definition log.c:614
static char l_new_default_format[]
Definition log.c:66
int yaz_log_module_level(const char *name)
returns level for module
Definition log.c:586
#define TIMEFORMAT_LEN
Definition log.c:67
FILE * yaz_log_file(void)
returns FILE handle for log or NULL if no file is in use
Definition log.c:138
static void yaz_log_open(void)
Definition log.c:396
void log_event_end(void(*func)(int, const char *, void *), void *info)
Definition log.c:310
Logging utility.
#define YLOG_APP2
log level: application 2
Definition log.h:60
#define YLOG_WARN
log level: warning
Definition log.h:46
#define YLOG_FATAL
log level: fatal
Definition log.h:42
#define YLOG_MALLOC
log level: malloc debug
Definition log.h:56
#define YLOG_ERRNO
log level: append system error message
Definition log.h:50
#define YLOG_FLUSH
log level: flush
Definition log.h:64
#define YLOG_DEFAULT_LEVEL
default log level
Definition log.h:72
#define YLOG_DEBUG
log level: debugging
Definition log.h:44
#define YLOG_APP
log level: application
Definition log.h:54
#define YLOG_NOTIME
log level: do not output date and time
Definition log.h:58
#define YLOG_TID
log level: append thread Id
Definition log.h:52
#define YLOG_ALL
Definition log.h:69
#define YLOG_LOGLVL
dynamic log level start
Definition log.h:66
#define YLOG_LOG
log level: log (regular)
Definition log.h:48
#define YLOG_APP3
log level: application 3
Definition log.h:62
#define YLOG_LAST_BIT
last bit for regular log bits . Rest are dynamic
Definition log.h:78
void yaz_mutex_leave(YAZ_MUTEX p)
leave critical section / AKA unlock
Definition mutex.c:123
void yaz_mutex_enter(YAZ_MUTEX p)
enter critical section / AKA lock
Definition mutex.c:72
void yaz_mutex_create(YAZ_MUTEX *p)
create MUTEX
Definition mutex.c:43
void yaz_mutex_destroy(YAZ_MUTEX *p)
destroy MUTEX
Definition mutex.c:141
Header for Mutex functions.
void yaz_vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
Definition snprintf.c:17
void yaz_snprintf(char *buf, size_t size, const char *fmt,...)
Definition snprintf.c:31
Header for config file reading utilities.
void yaz_thread_id_cstr(char *buf, size_t buf_max)
format current thread as printable C-string
Definition thread_id.c:32
Header for Mutex functions.
Header for memory handling functions.
Header for YAZ iconv interface.
#define yaz_isdigit(x)
Definition yaz-iconv.h:86
Header with fundamental macros.