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