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