2008-10-02 [colin] 3.5.0cvs144
[claws.git] / src / common / log.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #ifdef G_OS_WIN32
27 #  include <w32lib.h>
28 #endif
29
30 #include <stdio.h>
31 #include <glib.h>
32 #include <glib/gi18n.h>
33
34 #include "utils.h"
35 #include "log.h"
36 #include "hooks.h"
37
38 static FILE *log_fp[LOG_INSTANCE_MAX] = {
39         NULL,
40         NULL
41 };
42
43 static size_t log_size[LOG_INSTANCE_MAX] = {
44         0,
45         0
46 };
47
48 static gchar *log_filename[LOG_INSTANCE_MAX] = {
49         NULL,
50         NULL
51 };
52
53 typedef struct _LogInstanceData LogInstanceData;
54
55 struct _LogInstanceData {
56         const char *hook;
57         gchar *title;
58         int *prefs_logwin_width;
59         int *prefs_logwin_height;
60 };
61
62 static LogInstanceData log_instances[LOG_INSTANCE_MAX] = {
63         { LOG_APPEND_TEXT_HOOKLIST, NULL, NULL, NULL },
64         { DEBUG_FILTERING_APPEND_TEXT_HOOKLIST, NULL, NULL, NULL }
65 };
66
67 gboolean prefs_common_enable_log_standard(void);
68 gboolean prefs_common_enable_log_warning(void);
69 gboolean prefs_common_enable_log_error(void);
70 gboolean prefs_common_enable_log_status(void);
71
72 static gboolean invoke_hook_cb (gpointer data)
73 {
74         LogText *logtext = (LogText *)data;
75         hooks_invoke(get_log_hook(logtext->instance), logtext);
76         g_free(logtext->text);
77         g_free(logtext);
78         return FALSE;
79 }
80
81 void set_log_file(LogInstance instance, const gchar *filename)
82 {
83         gchar *fullname = NULL;
84         if (log_fp[instance])
85                 return;
86
87         if (!g_path_is_absolute(filename)) {
88                 fullname = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
89                                         filename, NULL);
90         } else {
91                 fullname = g_strdup(filename);
92         }
93         /* backup old logfile if existing */
94         if (is_file_exist(fullname)) {
95                 gchar *backupname;
96                 
97                 backupname = g_strconcat(fullname, ".bak", NULL);
98                 claws_unlink(backupname);
99                 if (g_rename(fullname, backupname) < 0)
100                         FILE_OP_ERROR(fullname, "rename");
101                 g_free(backupname);
102         }
103
104         log_fp[instance] = g_fopen(fullname, "wb");
105         if (!log_fp[instance]) {
106                 FILE_OP_ERROR(fullname, "fopen");
107                 log_filename[instance] = NULL;
108                 g_free(fullname);
109                 return;
110         }
111         log_filename[instance] = g_strdup(fullname);
112         log_size[instance] = 0;
113         g_free(fullname);
114 }
115
116 void close_log_file(LogInstance instance)
117 {
118         if (log_fp[instance]) {
119                 fclose(log_fp[instance]);
120                 log_fp[instance] = NULL;
121                 log_size[instance] = 0;
122                 g_free(log_filename[instance]);
123                 log_filename[instance] = NULL;
124         }
125 }
126
127 static void rotate_log(LogInstance instance)
128 {
129         if (log_size[instance] > 10 * 1024* 1024) {
130                 gchar *filename = g_strdup(log_filename[instance]);
131                 debug_print("rotating %s\n", filename);
132                 close_log_file(instance);
133                 set_log_file(instance, filename);
134                 g_free(filename);
135         }
136 }
137
138 const char *get_log_hook(LogInstance instance)
139 {
140         return log_instances[instance].hook;
141 }
142
143 void set_log_title(LogInstance instance, gchar *title)
144 {
145         log_instances[instance].title = title;
146 }
147
148 gchar *get_log_title(LogInstance instance)
149 {
150         return log_instances[instance].title;
151 }
152
153 void set_log_prefs(LogInstance instance, int* logwin_width, int* logwin_height)
154 {
155         log_instances[instance].prefs_logwin_width = logwin_width;
156         log_instances[instance].prefs_logwin_height = logwin_height;
157 }
158
159 void get_log_prefs(LogInstance instance, int** logwin_width, int** logwin_height)
160 {
161         if (logwin_width)
162                 *logwin_width = log_instances[instance].prefs_logwin_width;
163         if (logwin_height)
164                 *logwin_height = log_instances[instance].prefs_logwin_height;
165 }
166
167 void log_print(LogInstance instance, const gchar *format, ...)
168 {
169         va_list args;
170         gchar buf[BUFFSIZE + LOG_TIME_LEN];
171         time_t t;
172         LogText *logtext = g_new0(LogText, 1);
173         struct tm buft;
174
175         time(&t);
176         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
177
178         va_start(args, format);
179         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
180         va_end(args);
181
182         if (debug_get_mode()) g_print("%s", buf);
183
184         logtext->instance = instance;
185         logtext->text = g_strdup(buf);
186         logtext->type = LOG_NORMAL;
187         
188         g_timeout_add(0, invoke_hook_cb, logtext);
189
190         if (log_fp[instance] && prefs_common_enable_log_standard()) {
191                 int r;
192                 r = fputs(buf, log_fp[instance]);
193                 log_size[instance] += strlen(buf);
194                 r = fflush(log_fp[instance]);
195                 rotate_log(instance);
196         }
197 }
198
199 void log_message(LogInstance instance, const gchar *format, ...)
200 {
201         va_list args;
202         gchar buf[BUFFSIZE + LOG_TIME_LEN];
203         time_t t;
204         LogText *logtext = g_new0(LogText, 1);
205         struct tm buft;
206
207         time(&t);
208         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
209
210         va_start(args, format);
211         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
212         va_end(args);
213
214         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
215
216         logtext->instance = instance;
217         logtext->text = g_strdup(buf + LOG_TIME_LEN);
218         logtext->type = LOG_MSG;
219         
220         g_timeout_add(0, invoke_hook_cb, logtext);
221
222         if (log_fp[instance] && prefs_common_enable_log_standard()) {
223                 int r;
224                 r = fwrite(buf, 1, LOG_TIME_LEN, log_fp[instance]);
225                 r = fputs("* message: ", log_fp[instance]);
226                 log_size[instance] += strlen("* message: ");
227                 r = fputs(buf + LOG_TIME_LEN, log_fp[instance]);
228                 log_size[instance] += strlen(buf);
229                 r = fflush(log_fp[instance]);
230                 rotate_log(instance);
231         }
232 }
233
234 void log_warning(LogInstance instance, const gchar *format, ...)
235 {
236         va_list args;
237         gchar buf[BUFFSIZE + LOG_TIME_LEN];
238         time_t t;
239         LogText *logtext = g_new0(LogText, 1);
240         struct tm buft;
241
242         time(&t);
243         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
244
245         va_start(args, format);
246         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
247         va_end(args);
248
249         g_warning("%s", buf);
250
251         logtext->instance = instance;
252         logtext->text = g_strdup(buf + LOG_TIME_LEN);
253         logtext->type = LOG_WARN;
254         
255         g_timeout_add(0, invoke_hook_cb, logtext);
256
257         if (log_fp[instance] && prefs_common_enable_log_warning()) {
258                 int r;
259                 r = fwrite(buf, 1, LOG_TIME_LEN, log_fp[instance]);
260                 r = fputs("** warning: ", log_fp[instance]);
261                 log_size[instance] += strlen("** warning: ");
262                 r = fputs(buf + LOG_TIME_LEN, log_fp[instance]);
263                 log_size[instance] += strlen(buf);
264                 r = fflush(log_fp[instance]);
265                 rotate_log(instance);
266         }
267 }
268
269 void log_error(LogInstance instance, const gchar *format, ...)
270 {
271         va_list args;
272         gchar buf[BUFFSIZE + LOG_TIME_LEN];
273         time_t t;
274         LogText *logtext = g_new0(LogText, 1);
275         struct tm buft;
276
277         time(&t);
278         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
279
280         va_start(args, format);
281         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
282         va_end(args);
283
284         g_warning("%s", buf);
285
286         logtext->instance = instance;
287         logtext->text = g_strdup(buf + LOG_TIME_LEN);
288         logtext->type = LOG_ERROR;
289         
290         g_timeout_add(0, invoke_hook_cb, logtext);
291
292         if (log_fp[instance] && prefs_common_enable_log_error()) {
293                 int r;
294                 r = fwrite(buf, 1, LOG_TIME_LEN, log_fp[instance]);
295                 r = fputs("*** error: ", log_fp[instance]);
296                 log_size[instance] += strlen("*** error: ");
297                 r = fputs(buf + LOG_TIME_LEN, log_fp[instance]);
298                 log_size[instance] += strlen(buf);
299                 r = fflush(log_fp[instance]);
300                 rotate_log(instance);
301         }
302 }
303
304 void log_status_ok(LogInstance instance, const gchar *format, ...)
305 {
306         va_list args;
307         gchar buf[BUFFSIZE + LOG_TIME_LEN];
308         time_t t;
309         LogText *logtext = g_new0(LogText, 1);
310         struct tm buft;
311
312         time(&t);
313         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
314
315         va_start(args, format);
316         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
317         va_end(args);
318
319         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
320
321         logtext->instance = instance;
322         logtext->text = g_strdup(buf + LOG_TIME_LEN);
323         logtext->type = LOG_STATUS_OK;
324         
325         g_timeout_add(0, invoke_hook_cb, logtext);
326
327         if (log_fp[instance] && prefs_common_enable_log_status()) {
328                 int r;
329                 r = fwrite(buf, 1, LOG_TIME_LEN, log_fp[instance]);
330                 r = fputs("* OK: ", log_fp[instance]);
331                 log_size[instance] += strlen("* OK: ");
332                 r = fputs(buf + LOG_TIME_LEN, log_fp[instance]);
333                 log_size[instance] += strlen(buf);
334                 r = fflush(log_fp[instance]);
335                 rotate_log(instance);
336         }
337 }
338
339 void log_status_nok(LogInstance instance, const gchar *format, ...)
340 {
341         va_list args;
342         gchar buf[BUFFSIZE + LOG_TIME_LEN];
343         time_t t;
344         LogText *logtext = g_new0(LogText, 1);
345         struct tm buft;
346
347         time(&t);
348         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
349
350         va_start(args, format);
351         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
352         va_end(args);
353
354         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
355
356         logtext->instance = instance;
357         logtext->text = g_strdup(buf + LOG_TIME_LEN);
358         logtext->type = LOG_STATUS_NOK;
359         
360         g_timeout_add(0, invoke_hook_cb, logtext);
361
362         if (log_fp[instance] && prefs_common_enable_log_status()) {
363                 int r;
364                 r = fwrite(buf, 1, LOG_TIME_LEN, log_fp[instance]);
365                 r = fputs("* NOT OK: ", log_fp[instance]);
366                 log_size[instance] += strlen("* NOT OK: ");
367                 r = fputs(buf + LOG_TIME_LEN, log_fp[instance]);
368                 log_size[instance] += strlen(buf);
369                 r = fflush(log_fp[instance]);
370                 rotate_log(instance);
371         }
372 }
373
374 void log_status_skip(LogInstance instance, const gchar *format, ...)
375 {
376         va_list args;
377         gchar buf[BUFFSIZE + LOG_TIME_LEN];
378         time_t t;
379         LogText *logtext = g_new0(LogText, 1);
380         struct tm buft;
381
382         time(&t);
383         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
384
385         va_start(args, format);
386         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
387         va_end(args);
388
389         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
390
391         logtext->instance = instance;
392         logtext->text = g_strdup(buf + LOG_TIME_LEN);
393         logtext->type = LOG_STATUS_SKIP;
394         
395         g_timeout_add(0, invoke_hook_cb, logtext);
396
397         if (log_fp[instance] && prefs_common_enable_log_status()) {
398                 int r;
399                 r = fwrite(buf, 1, LOG_TIME_LEN, log_fp[instance]);
400                 r = fputs("* SKIPPED: ", log_fp[instance]);
401                 log_size[instance] += strlen("* SKIPPED: ");
402                 r = fputs(buf + LOG_TIME_LEN, log_fp[instance]);
403                 log_size[instance] += strlen(buf);
404                 r = fflush(log_fp[instance]);
405                 rotate_log(instance);
406         }
407 }