00001
00002
00003
00004
00005
00011 #if HAVE_CONFIG_H
00012 #include <config.h>
00013 #endif
00014
00015 #ifdef WIN32
00016 #include <windows.h>
00017 #include <sys/stat.h>
00018 #endif
00019
00020 #if HAVE_SYS_STAT_H
00021 #include <sys/stat.h>
00022 #endif
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <ctype.h>
00026 #include <string.h>
00027 #include <stdarg.h>
00028 #include <errno.h>
00029 #include <time.h>
00030 #include <yaz/nmem.h>
00031 #include <yaz/log.h>
00032 #include <yaz/snprintf.h>
00033 #include <yaz/xmalloc.h>
00034
00035 #define HAS_STRERROR 1
00036
00037
00038 #if HAS_STRERROR
00039
00040 #else
00041 char *strerror(int n)
00042 {
00043 extern char *sys_errlist[];
00044 return sys_errlist[n];
00045 }
00046
00047 #endif
00048
00049
00050 static int l_level = YLOG_DEFAULT_LEVEL;
00051
00052 enum l_file_type { use_stderr, use_none, use_file };
00053 static enum l_file_type yaz_file_type = use_stderr;
00054 static FILE *yaz_global_log_file = NULL;
00055
00056 static void (*start_hook_func)(int, const char *, void *) = NULL;
00057 static void *start_hook_info;
00058
00059 static void (*end_hook_func)(int, const char *, void *) = NULL;
00060 static void *end_hook_info;
00061
00062 static void (*hook_func)(int, const char *, void *) = NULL;
00063 static void *hook_info;
00064
00065 static char l_prefix[512] = "";
00066 static char l_prefix2[512] = "";
00067 static char l_fname[512] = "";
00068
00069
00070 static char l_old_default_format[] = "%H:%M:%S-%d/%m";
00071 static char l_new_default_format[] = "%Y%m%d-%H%M%S";
00072 #define TIMEFORMAT_LEN 50
00073 static char l_custom_format[TIMEFORMAT_LEN] = "";
00074 static char *l_actual_format = l_old_default_format;
00075
00080 static int l_max_size = 1024*1024*1024;
00081
00082 #define MAX_MASK_NAMES 35
00083 static struct {
00084 int mask;
00085 char *name;
00086 } mask_names[MAX_MASK_NAMES] =
00087 {
00088 { YLOG_FATAL, "fatal"},
00089 { YLOG_DEBUG, "debug"},
00090 { YLOG_WARN, "warn" },
00091 { YLOG_LOG, "log" },
00092 { YLOG_ERRNO, ""},
00093 { YLOG_MALLOC, "malloc"},
00094 { YLOG_APP, "app" },
00095 { YLOG_NOTIME, "notime" },
00096 { YLOG_APP2, "app2" },
00097 { YLOG_APP3, "app3" },
00098 { YLOG_ALL, "all" },
00099 { YLOG_FLUSH, "flush" },
00100 { YLOG_LOGLVL, "loglevel" },
00101 { 0, "none" },
00102 { 0, NULL }
00103
00104 };
00105
00106 static unsigned int next_log_bit = YLOG_LAST_BIT<<1;
00107
00108 static void internal_log_init(void)
00109 {
00110 static int mutex_init_flag = 0;
00111 char *env;
00112
00113 if (mutex_init_flag)
00114 return;
00115 mutex_init_flag = 1;
00116
00117 env = getenv("YAZ_LOG");
00118 if (env)
00119 l_level = yaz_log_mask_str_x(env, l_level);
00120 }
00121
00122
00123 FILE *yaz_log_file(void)
00124 {
00125 FILE *f = 0;
00126 switch(yaz_file_type)
00127 {
00128 case use_stderr: f = stderr; break;
00129 case use_none: f = 0; break;
00130 case use_file: f = yaz_global_log_file; break;
00131 }
00132 return f;
00133 }
00134
00135 void yaz_log_close(void)
00136 {
00137 if (yaz_file_type == use_file && yaz_global_log_file)
00138 {
00139 fclose(yaz_global_log_file);
00140 yaz_global_log_file = 0;
00141 }
00142 }
00143
00144 void yaz_log_init_file(const char *fname)
00145 {
00146 internal_log_init();
00147
00148 yaz_log_close();
00149 if (fname)
00150 {
00151 if (*fname == '\0')
00152 yaz_file_type = use_stderr;
00153 else
00154 yaz_file_type = use_file;
00155 strncpy(l_fname, fname, sizeof(l_fname)-1);
00156 l_fname[sizeof(l_fname)-1] = '\0';
00157 }
00158 else
00159 {
00160 yaz_file_type = use_none;
00161 l_fname[0] = '\0';
00162 }
00163 yaz_log_reopen();
00164 }
00165
00166 static void rotate_log(const char *cur_fname)
00167 {
00168 int i;
00169
00170 #ifdef WIN32
00171
00172 yaz_log_close();
00173 #endif
00174 for (i = 0; i<9; i++)
00175 {
00176 char fname_str[FILENAME_MAX];
00177 struct stat stat_buf;
00178
00179 yaz_snprintf(fname_str, sizeof(fname_str), "%s.%d", cur_fname, i);
00180 if (stat(fname_str, &stat_buf) != 0)
00181 break;
00182 }
00183 for (; i >= 0; --i)
00184 {
00185 char fname_str[2][FILENAME_MAX];
00186
00187 if (i > 0)
00188 yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
00189 "%s.%d", cur_fname, i-1);
00190 else
00191 yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
00192 "%s", cur_fname);
00193 yaz_snprintf(fname_str[1], sizeof(fname_str[1]),
00194 "%s.%d", cur_fname, i);
00195 #ifdef WIN32
00196 MoveFileEx(fname_str[0], fname_str[1], MOVEFILE_REPLACE_EXISTING);
00197 #else
00198 rename(fname_str[0], fname_str[1]);
00199 #endif
00200 }
00201 }
00202
00203
00204 void yaz_log_init_level(int level)
00205 {
00206 internal_log_init();
00207 if ( (l_level & YLOG_FLUSH) != (level & YLOG_FLUSH) )
00208 {
00209 l_level = level;
00210 yaz_log_reopen();
00211 }
00212 else
00213 l_level = level;
00214
00215 if (l_level & YLOG_LOGLVL)
00216 {
00217 const char *bittype = "Static ";
00218 int i, sz;
00219
00220 yaz_log(YLOG_LOGLVL, "Setting log level to %d = 0x%08x",
00221 l_level, l_level);
00222
00223 for (sz = 0; mask_names[sz].name; sz++)
00224 ;
00225
00226 for (i = 0; i < sz; i++)
00227 if (mask_names[i].mask && *mask_names[i].name)
00228 if (strcmp(mask_names[i].name, "all") != 0)
00229 {
00230 yaz_log(YLOG_LOGLVL, "%s log bit %08x '%s' is %s",
00231 bittype, mask_names[i].mask, mask_names[i].name,
00232 (level & mask_names[i].mask)? "ON": "off");
00233 if (mask_names[i].mask > YLOG_LAST_BIT)
00234 bittype = "Dynamic";
00235 }
00236 }
00237 }
00238
00239 void yaz_log_init_prefix(const char *prefix)
00240 {
00241 if (prefix && *prefix)
00242 yaz_snprintf(l_prefix, sizeof(l_prefix), "%s ", prefix);
00243 else
00244 *l_prefix = 0;
00245 }
00246
00247 void yaz_log_init_prefix2(const char *prefix)
00248 {
00249 if (prefix && *prefix)
00250 yaz_snprintf(l_prefix2, sizeof(l_prefix2), "%s ", prefix);
00251 else
00252 *l_prefix2 = 0;
00253 }
00254
00255 void yaz_log_init(int level, const char *prefix, const char *fname)
00256 {
00257 internal_log_init();
00258 yaz_log_init_level(level);
00259 yaz_log_init_prefix(prefix);
00260 if (fname && *fname)
00261 yaz_log_init_file(fname);
00262 }
00263
00264 void yaz_log_init_max_size(int mx)
00265 {
00266 if (mx > 0)
00267 l_max_size = mx;
00268 else
00269 l_max_size = 0;
00270 }
00271
00272 void yaz_log_set_handler(void (*func)(int, const char *, void *), void *info)
00273 {
00274 hook_func = func;
00275 hook_info = info;
00276 }
00277
00278 void log_event_start(void (*func)(int, const char *, void *), void *info)
00279 {
00280 start_hook_func = func;
00281 start_hook_info = info;
00282 }
00283
00284 void log_event_end(void (*func)(int, const char *, void *), void *info)
00285 {
00286 end_hook_func = func;
00287 end_hook_info = info;
00288 }
00289
00290 static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
00291 {
00292 char new_filename[512];
00293 static char cur_filename[512] = "";
00294
00295 if (yaz_file_type != use_file)
00296 return;
00297
00298 if (*l_fname)
00299 {
00300 strftime(new_filename, sizeof(new_filename)-1, l_fname, tm);
00301 if (strcmp(new_filename, cur_filename))
00302 {
00303 strcpy(cur_filename, new_filename);
00304 force = 1;
00305 }
00306 }
00307
00308 if (l_max_size > 0 && yaz_global_log_file)
00309 {
00310 long flen = ftell(yaz_global_log_file);
00311 if (flen > l_max_size)
00312 {
00313 rotate_log(cur_filename);
00314 force = 1;
00315 }
00316 }
00317 if (force && *cur_filename)
00318 {
00319 FILE *new_file;
00320 #ifdef WIN32
00321 yaz_log_close();
00322 #endif
00323 new_file = fopen(cur_filename, filemode);
00324 if (new_file)
00325 {
00326 yaz_log_close();
00327 yaz_global_log_file = new_file;
00328 if (l_level & YLOG_FLUSH)
00329 setvbuf(yaz_global_log_file, 0, _IONBF, 0);
00330 }
00331 else
00332 {
00333
00334 l_max_size = 0;
00335 }
00336 }
00337 }
00338
00339 static void yaz_log_do_reopen(const char *filemode)
00340 {
00341 time_t cur_time = time(0);
00342 #if HAVE_LOCALTIME_R
00343 struct tm tm0, *tm = &tm0;
00344 #else
00345 struct tm *tm;
00346 #endif
00347
00348 #if HAVE_LOCALTIME_R
00349 localtime_r(&cur_time, tm);
00350 #else
00351 tm = localtime(&cur_time);
00352 #endif
00353 yaz_log_open_check(tm, 1, filemode);
00354 }
00355
00356
00357 void yaz_log_reopen()
00358 {
00359 yaz_log_do_reopen("a");
00360 }
00361
00362 void yaz_log_trunc()
00363 {
00364 yaz_log_do_reopen("w");
00365 }
00366
00367 static void yaz_strftime(char *dst, size_t sz,
00368 const char *fmt, const struct tm *tm)
00369 {
00370 strftime(dst, sz, fmt, tm);
00371 }
00372
00373 static void yaz_log_to_file(int level, const char *log_message)
00374 {
00375 FILE *file;
00376 time_t ti = time(0);
00377 #if HAVE_LOCALTIME_R
00378 struct tm tm0, *tm = &tm0;
00379 #else
00380 struct tm *tm;
00381 #endif
00382
00383 internal_log_init();
00384
00385 #if HAVE_LOCALTIME_R
00386 localtime_r(&ti, tm);
00387 #else
00388 tm = localtime(&ti);
00389 #endif
00390
00391 yaz_log_open_check(tm, 0, "a");
00392 file = yaz_log_file();
00393
00394 if (file)
00395 {
00396 char tbuf[TIMEFORMAT_LEN];
00397 char flags[1024];
00398 int i;
00399
00400 *flags = '\0';
00401 for (i = 0; level && mask_names[i].name; i++)
00402 if ( mask_names[i].mask & level)
00403 {
00404 if (*mask_names[i].name && mask_names[i].mask &&
00405 mask_names[i].mask != YLOG_ALL)
00406 {
00407 if (strlen(flags) + strlen(mask_names[i].name)
00408 < sizeof(flags) - 4)
00409 {
00410 strcat(flags, "[");
00411 strcat(flags, mask_names[i].name);
00412 strcat(flags, "]");
00413 }
00414 level &= ~mask_names[i].mask;
00415 }
00416 }
00417
00418 tbuf[0] = '\0';
00419 if (!(l_level & YLOG_NOTIME))
00420 {
00421 yaz_strftime(tbuf, TIMEFORMAT_LEN-2, l_actual_format, tm);
00422 tbuf[TIMEFORMAT_LEN-2] = '\0';
00423 }
00424 if (tbuf[0])
00425 strcat(tbuf, " ");
00426 fprintf(file, "%s%s%s %s%s\n", tbuf, l_prefix, flags, l_prefix2,
00427 log_message);
00428 if (l_level & YLOG_FLUSH)
00429 fflush(file);
00430 }
00431 }
00432
00433 void yaz_log(int level, const char *fmt, ...)
00434 {
00435 va_list ap;
00436 char buf[4096];
00437 FILE *file;
00438 int o_level = level;
00439
00440 internal_log_init();
00441 if (!(level & l_level))
00442 return;
00443 va_start(ap, fmt);
00444
00445
00446 yaz_vsnprintf(buf, sizeof(buf)-30, fmt, ap);
00447 if (strlen(buf) >= sizeof(buf)-31)
00448 strcat(buf, " [rest of output omitted]");
00449
00450 if (o_level & YLOG_ERRNO)
00451 {
00452 int remain = sizeof(buf) - strlen(buf);
00453 if (remain > 100)
00454 {
00455 strcat(buf, " [");
00456 yaz_strerror(buf+strlen(buf), remain-5);
00457 strcat(buf, "]");
00458 }
00459 }
00460 va_end (ap);
00461 if (start_hook_func)
00462 (*start_hook_func)(o_level, buf, start_hook_info);
00463 if (hook_func)
00464 (*hook_func)(o_level, buf, hook_info);
00465 file = yaz_log_file();
00466 if (file)
00467 yaz_log_to_file(level, buf);
00468 if (end_hook_func)
00469 (*end_hook_func)(o_level, buf, end_hook_info);
00470 }
00471
00472 void yaz_log_time_format(const char *fmt)
00473 {
00474 if ( !fmt || !*fmt)
00475 {
00476 l_actual_format = l_new_default_format;
00477 return;
00478 }
00479 if (0==strcmp(fmt, "old"))
00480 {
00481 l_actual_format = l_old_default_format;
00482 return;
00483 }
00484
00485 strncpy(l_custom_format, fmt, TIMEFORMAT_LEN-1);
00486 l_custom_format[TIMEFORMAT_LEN-1] = '\0';
00487 l_actual_format = l_custom_format;
00488 }
00489
00491 static char *clean_name(const char *name, int len, char *namebuf, int buflen)
00492 {
00493 char *p = namebuf;
00494 char *start = namebuf;
00495 if (buflen <= len)
00496 len = buflen-1;
00497 strncpy(namebuf, name, len);
00498 namebuf[len] = '\0';
00499 while ((p = strchr(start, '/')))
00500 start = p+1;
00501 if ((p = strrchr(start, '.')))
00502 *p = '\0';
00503 return start;
00504 }
00505
00506 static int define_module_bit(const char *name)
00507 {
00508 int i;
00509
00510 for (i = 0; mask_names[i].name; i++)
00511 if (0 == strcmp(mask_names[i].name, name))
00512 {
00513 return mask_names[i].mask;
00514 }
00515 if ( (i>=MAX_MASK_NAMES) || (next_log_bit & (1<<31) ))
00516 {
00517 yaz_log(YLOG_WARN, "No more log bits left, not logging '%s'", name);
00518 return 0;
00519 }
00520 mask_names[i].mask = next_log_bit;
00521 next_log_bit = next_log_bit<<1;
00522 mask_names[i].name = (char *) malloc(strlen(name)+1);
00523 strcpy(mask_names[i].name, name);
00524 mask_names[i+1].name = NULL;
00525 mask_names[i+1].mask = 0;
00526 return mask_names[i].mask;
00527 }
00528
00529 int yaz_log_module_level(const char *name)
00530 {
00531 int i;
00532 char clean[255];
00533 char *n = clean_name(name, strlen(name), clean, sizeof(clean));
00534 internal_log_init();
00535
00536 for (i = 0; mask_names[i].name; i++)
00537 if (0==strcmp(n, mask_names[i].name))
00538 {
00539 yaz_log(YLOG_LOGLVL, "returning log bit 0x%x for '%s' %s",
00540 mask_names[i].mask, n,
00541 strcmp(n,name) ? name : "");
00542 return mask_names[i].mask;
00543 }
00544 yaz_log(YLOG_LOGLVL, "returning NO log bit for '%s' %s", n,
00545 strcmp(n, name) ? name : "" );
00546 return 0;
00547 }
00548
00549 int yaz_log_mask_str(const char *str)
00550 {
00551 internal_log_init();
00552 return yaz_log_mask_str_x(str, l_level);
00553 }
00554
00555 int yaz_log_mask_str_x(const char *str, int level)
00556 {
00557 const char *p;
00558
00559 internal_log_init();
00560 while (*str)
00561 {
00562 int negated = 0;
00563 for (p = str; *p && *p != ','; p++)
00564 ;
00565 if (*str=='-')
00566 {
00567 negated = 1;
00568 str++;
00569 }
00570 if (isdigit(*(unsigned char *) str))
00571 {
00572 level = atoi(str);
00573 }
00574 else
00575 {
00576 char clean[509];
00577 char *n = clean_name(str, p-str, clean, sizeof(clean));
00578 int mask = define_module_bit(n);
00579 if (!mask)
00580 level = 0;
00581 else if (negated)
00582 level &= ~mask;
00583 else
00584 level |= mask;
00585 }
00586 if (*p == ',')
00587 p++;
00588 str = p;
00589 }
00590 return level;
00591 }
00592
00593
00594
00595
00596
00597
00598