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