Rotating logfile if exceeding a gigabyte
[yaz-moved-to-github.git] / src / log.c
1 /*
2  * Copyright (c) 1995-2004, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: log.c,v 1.7 2004-11-02 14:13:09 heikki Exp $
6  */
7
8 /**
9  * \file log.c
10  * \brief Implements logging utility
11  */
12
13 #if HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #ifdef WIN32
18 #include <windows.h>
19 #endif
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <errno.h>
27 #include <time.h>
28 #include <yaz/nmem.h>
29 #include <yaz/log.h>
30
31 #define HAS_STRERROR 1
32
33 #if HAS_STRERROR
34
35 #else
36 char *strerror(int n)
37 {
38         extern char *sys_errlist[];
39         return sys_errlist[n];
40 }
41
42 #endif
43
44
45 static int l_level = LOG_DEFAULT_LEVEL;
46 static FILE *l_file = NULL;
47 static char l_prefix[512] = "";
48 static char l_prefix2[512] = "";
49 static char l_fname[512] = "";
50
51 static char l_old_default_format[]="%H:%M:%S-%d/%m";
52 static char l_new_default_format[]="%Y%m%d-%H%M%S";
53 #define TIMEFORMAT_LEN 50
54 static char l_custom_format[TIMEFORMAT_LEN]="";
55 static char *l_actual_format=l_old_default_format;
56
57 /** l_max_size tells when to rotate the log. Default to 1 GB */
58 static int l_max_size=1024*1024*1024;
59 /* static int l_max_size=1024; */  /* while testing */
60
61 static struct {
62     int mask;
63     char *name;
64 } mask_names[] =
65 {
66     { LOG_FATAL, "fatal"},
67     { LOG_DEBUG, "debug"},
68     { LOG_WARN,  "warn" },
69     { LOG_LOG,   "log"  },
70     { LOG_ERRNO, ""},
71     { LOG_MALLOC, "malloc"},
72     { LOG_APP,   "app"  },
73     { LOG_NOTIME, "" },
74     { LOG_APP2  , "app2" },
75     { LOG_APP3  , "app3" },
76     { LOG_ALL,   "all"  },
77     { LOG_FLUSH, "flush" },
78     { 0,         "none" },
79     { 0, NULL }
80 };  
81
82 FILE *yaz_log_file(void)
83 {
84     if (!l_file)
85         l_file = stderr;
86     return l_file;
87 }
88
89 void yaz_log_init_file (const char *fname)
90 {
91     if (fname)
92     {
93         strncpy(l_fname, fname, sizeof(l_fname)-1);
94         l_fname[sizeof(l_fname)-1] = '\0';
95     }
96     else
97         l_fname[0] = '\0';
98     yaz_log_reopen();
99 }
100
101 void yaz_log_reopen(void)
102 {
103     FILE *new_file;
104     if (!l_file)
105         l_file = stderr;
106
107     if (!*l_fname)
108         new_file=stderr;
109     else if (!(new_file = fopen(l_fname, "a")))
110         return;
111     if (l_file != stderr)
112     {
113         fclose (l_file);
114     }
115     if (l_level & LOG_FLUSH)
116         setvbuf(new_file, 0, _IONBF, 0);
117     l_file = new_file;
118 }
119
120 static void rotate_log()
121 {
122     char newname[512];
123     if (l_file==stderr)
124         return; /* can't rotate that */
125     if (!*l_fname)
126         return; /* hmm, no name, can't rotate */
127     strncpy(newname, l_fname, 510);
128     newname[510]='\0'; /* make sure it is terminated */
129     strcat(newname,".1");
130 #ifdef WIN32
131     fclose(l_file);
132     l_file=stderr;
133 #endif
134     rename(l_fname, newname);
135     yaz_log_reopen();
136 }
137
138
139 void yaz_log_init_level (int level)
140 {
141     if ( (l_level & LOG_FLUSH) != (level & LOG_FLUSH) )
142     {
143         l_level = level;
144         yaz_log_reopen(); /* make sure we set buffering right */
145     } else
146         l_level = level;
147 }
148
149 void yaz_log_init_prefix (const char *prefix)
150 {
151     if (prefix && *prefix)
152         sprintf(l_prefix, "%.511s ", prefix);
153     else
154         *l_prefix = 0;
155 }
156
157 void yaz_log_init_prefix2 (const char *prefix)
158 {
159     if (prefix && *prefix)
160         sprintf(l_prefix2, "%.511s ", prefix);
161     else
162         *l_prefix2 = 0;
163 }
164
165 void yaz_log_init(int level, const char *prefix, const char *fname)
166 {
167     yaz_log_init_level (level);
168     yaz_log_init_prefix (prefix);
169     if (fname && *fname)
170         yaz_log_init_file (fname);
171 }
172
173 static void (*start_hook_func)(int, const char *, void *) = NULL;
174 static void *start_hook_info;
175 static void (*end_hook_func)(int, const char *, void *) = NULL;
176 static void *end_hook_info;
177
178 void log_event_start (void (*func)(int, const char *, void *), void *info)
179 {
180     start_hook_func = func;
181     start_hook_info = info;
182 }
183
184 void log_event_end (void (*func)(int, const char *, void *), void *info)
185 {
186     end_hook_func = func;
187     end_hook_info = info;
188 }
189
190 void yaz_log(int level, const char *fmt, ...)
191 {
192     va_list ap;
193     char buf[4096], flags[1024];
194     int i;
195     time_t ti;
196     struct tm *tim;
197     char tbuf[TIMEFORMAT_LEN]="";
198     int o_level = level;
199     int flen; 
200
201     if (!(level & l_level))
202         return;
203     if (!l_file)
204         l_file = stderr;
205     
206     if (l_file != stderr)
207     {
208         flen=ftell(l_file);
209         if (flen>l_max_size) 
210             rotate_log();
211     }
212
213     *flags = '\0';
214     for (i = 0; level && mask_names[i].name; i++)
215         if (mask_names[i].mask & level)
216         {
217             if (*mask_names[i].name)
218                 sprintf(flags + strlen(flags), "[%s]", mask_names[i].name);
219             level -= mask_names[i].mask;
220         }
221     va_start(ap, fmt);
222 #ifdef WIN32
223     _vsnprintf(buf, sizeof(buf)-1, fmt, ap);
224 #else
225 /* !WIN32 */
226 #if HAVE_VSNPRINTF
227     vsnprintf(buf, sizeof(buf), fmt, ap);
228 #else
229     vsprintf(buf, fmt, ap);
230 #endif
231 #endif
232 /* WIN32 */
233     if (o_level & LOG_ERRNO)
234     {
235         strcat(buf, " [");
236         yaz_strerror(buf+strlen(buf), 2048);
237         strcat(buf, "]");
238     }
239     va_end (ap);
240     if (start_hook_func)
241         (*start_hook_func)(o_level, buf, start_hook_info);
242     ti = time(0);
243     tim = localtime(&ti);
244     if (l_level & LOG_NOTIME)
245       tbuf[0]='\0';
246     else
247       strftime(tbuf, TIMEFORMAT_LEN-1, l_actual_format, tim);
248     tbuf[TIMEFORMAT_LEN-1]='\0';
249     fprintf(l_file, "%s %s%s %s%s\n", tbuf, l_prefix, flags,
250             l_prefix2, buf);
251     if (l_level & (LOG_FLUSH|LOG_DEBUG) )
252         fflush(l_file);
253     if (end_hook_func)
254         (*end_hook_func)(o_level, buf, end_hook_info);
255 }
256
257 void yaz_log_time_format(const char *fmt)
258 {
259     if ( !fmt || !*fmt) 
260     { /* no format, default to new */
261         l_actual_format = l_new_default_format;
262         return; 
263     }
264     if (0==strcmp(fmt,"old"))
265     { /* force the old format */
266         l_actual_format = l_old_default_format;
267         return; 
268     }
269     /* else use custom format */
270     strncpy(l_custom_format, fmt, TIMEFORMAT_LEN-1);
271     l_custom_format[TIMEFORMAT_LEN-1]='\0';
272     l_actual_format = l_custom_format;
273 }
274
275 int yaz_log_mask_str (const char *str)
276 {
277     return yaz_log_mask_str_x (str, LOG_DEFAULT_LEVEL);
278 }
279
280 int yaz_log_mask_str_x (const char *str, int level)
281 {
282     const char *p;
283     int i;
284
285     while (*str)
286     {
287         for (p = str; *p && *p != ','; p++)
288             ;
289         if (*str == '-' || isdigit(*str))
290             level = atoi (str);
291         else
292             for (i = 0; mask_names[i].name; i++)
293                 if (strlen (mask_names[i].name) == (size_t) (p-str) &&
294                     memcmp (mask_names[i].name, str, p-str) == 0)
295                 {
296                     if (mask_names[i].mask)
297                         level |= mask_names[i].mask;
298                     else
299                         level = 0;
300                 }
301         if (*p == ',')
302             p++;
303         str = p;
304     }
305     return level;
306 }