]> sjero.net Git - linphone/blob - gtk/logging.c
Fix upnp forgotten retain
[linphone] / gtk / logging.c
1 /*
2 linphone, gtk-glade interface.
3 Copyright (C) 2008  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (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, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #include "linphone.h"
21
22 #ifndef WIN32
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #endif
26
27 extern gchar *linphone_logfile;
28
29 static GtkWidget *log_window=NULL;
30 static GStaticMutex log_mutex=G_STATIC_MUTEX_INIT;
31 static GList *log_queue=NULL;
32 static const char *dateformat="%Y%m%d-%H:%M:%S";
33
34 #define LOG_MAX_CHARS 1000000  /*1 mega bytes of traces*/
35
36 typedef struct _LinphoneGtkLog{
37         OrtpLogLevel lev;
38         gchar *msg;
39 }LinphoneGtkLog;
40
41
42 /******
43  * Module to log to a file
44  ******/
45
46 /* Marker to insert as a line at the start of every log file */
47 #define LOGFILE_MARKER_START  "----<start>----"
48 /* Marker to insert as a line at the end of every log file */
49 #define LOGFILE_MARKER_STOP  "----<end>----"
50 /* Number of files to keep in history, log file rotation will be
51    performed. */
52 #define LOGFILE_ROTATION 4
53 /* Pointer to opened log file */
54 static FILE *_logfile = NULL;
55
56
57 /* Called on exit, print out the marker, close the file and avoid to
58    continue logging. */
59 void linphone_gtk_log_uninit()
60 {
61         if (_logfile != NULL) {
62                 fprintf(_logfile, "%s\n", LOGFILE_MARKER_STOP);
63                 fclose(_logfile);
64                 _logfile = NULL;
65         }
66 }
67
68 /* Called when we start logging, find a good place for the log files,
69    perform rotation, insert the start marker and return the pointer to
70    the file that should be used for logging, or NULL on errors or if
71    disabled. */
72 static FILE *linphone_gtk_log_init()
73 {
74         static char _logdir[1024];
75         static char _logfname[1024];
76         static gboolean _log_init = FALSE;
77         const char *dst_fname=NULL;
78
79         if (!_log_init) {
80                 if (linphone_gtk_get_core()!=NULL){
81                         dst_fname = linphone_gtk_get_ui_config("logfile",NULL);
82                         dateformat=linphone_gtk_get_ui_config("logfile_date_format",dateformat);
83                 }
84                 /* For anything to happen, we need a logfile configuration variable,
85                  this is our trigger */
86                 if (dst_fname) {
87                         /* arrange for _logdir to contain a
88                          directory that has been created and _logfname to contain the
89                          path to a file to which we will log */
90 #ifdef WIN32
91                         const char *appdata=getenv("LOCALAPPDATA");
92                         if (appdata) {
93                                 snprintf(_logdir, sizeof(_logdir),"%s\\Linphone", appdata);
94                                 mkdir(_logdir);
95                         } else {
96                                 _logdir[0] = '\0';
97                         }
98 #define PATH_SEPARATOR '\\'
99 #else
100                         const char *home=getenv("HOME");
101                         if (home) {
102                                 snprintf(_logdir, sizeof(_logdir),"%s/.linphone", home);
103                                 mkdir(_logdir,S_IRUSR | S_IWUSR | S_IRGRP);
104                         } else {
105                                 _logdir[0] = '\0';
106                         }
107 #define PATH_SEPARATOR '/'
108 #endif
109                         if (_logdir[0] != '\0') {
110                                 /* We have a directory, fix the path to the log file in it and
111                                  open the file so that we will be appending to it. */
112                                 snprintf(_logfname, sizeof(_logfname), "%s%c%s",_logdir, PATH_SEPARATOR, dst_fname);
113                         }
114                 }else if (linphone_logfile!=NULL){
115                         snprintf(_logfname,sizeof(_logfname),"%s",linphone_logfile);
116                 }
117                 
118                 if (_logfname[0]!='\0'){
119                         /* If the constant LOGFILE_ROTATION is greater than zero, then
120                          we kick away a simple rotation that will ensure that there
121                          are never more than LOGFILE_ROTATION+1 old copies of the
122                          log file on the disk.  The oldest file is always rotated
123                          "away" as expected.  Rotated files have the same name as
124                          the main log file, though with a number 0..LOGFILE_ROTATION
125                          at the end, where the greater the number is, the older the
126                          file is. */
127                         if (ortp_file_exist(_logfname)==0 && LOGFILE_ROTATION > 0) {
128                                 int i;
129                                 char old_fname[1024];
130                                 char new_fname[1024];
131
132                                 /* Rotate away existing files.  We make sure to remove the
133                                  old files otherwise rename() would not work properly.  We
134                                  have to loop in reverse here. */
135                                 for (i=LOGFILE_ROTATION-1;i>=0;i--) {
136                                         snprintf(old_fname, sizeof(old_fname), "%s%c%s.%d",
137                                                 _logdir, PATH_SEPARATOR, dst_fname, i);
138                                         snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
139                                                 _logdir, PATH_SEPARATOR, dst_fname, i+1);
140                                         if (ortp_file_exist(old_fname)==0) {
141                                                 if (ortp_file_exist(new_fname)==0)
142                                                         unlink(new_fname);
143                                                 rename(old_fname, new_fname);
144                                         }
145                                 }
146                                 /* Move current log file as the first of the rotation.  Make
147                                  sure to remove the old .0 also, since otherwise rename()
148                                  would not work as expected. */
149                                 snprintf(new_fname, sizeof(new_fname), "%s%c%s.%d",
150                                         _logdir, PATH_SEPARATOR, dst_fname, 0);
151                                 if (ortp_file_exist(new_fname)==0)
152                                         unlink(new_fname);
153                                 rename(_logfname, new_fname);
154                         }
155                         /* Start a new log file and mark that we have now initialised */
156                         _logfile = fopen(_logfname, "w");
157                         fprintf(_logfile, "%s\n", LOGFILE_MARKER_START);
158                 }
159                 _log_init = TRUE;
160         }
161         return _logfile;
162 }
163
164 static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg)
165 {
166         time_t now;
167         FILE *outlog;
168
169         outlog = linphone_gtk_log_init();
170         if (outlog != NULL) {
171                 /* We have an opened file and we have initialised properly, it's
172                  time to write all these log messages. We convert the log level
173                  from oRTP into something readable and timestamp each log
174                  message.  The format of the time       stamp can be controlled by
175                  logfile_date_format in the GtkUi section of the config file,
176                  but it defaults to something compact, but yet readable. */
177                 const char *lname="undef";
178                 char date[256];
179
180                 /* Convert level constant to text */
181                 switch(lev){
182                         case ORTP_DEBUG:
183                                 lname="debug";
184                                 break;
185                         case ORTP_MESSAGE:
186                                 lname="message";
187                                 break;
188                         case ORTP_WARNING:
189                                 lname="warning";
190                                 break;
191                         case ORTP_ERROR:
192                                 lname="error";
193                                 break;
194                         case ORTP_FATAL:
195                                 lname="fatal";
196                                 break;
197                         default:
198                                 lname="undef";
199                                 break;
200                 }
201                 /* Get current time and format it properly */
202                 now = time(NULL);
203                 strftime(date, sizeof(date), dateformat, localtime(&now));
204                 /* Now print out the message to the logfile.  We don't flush,
205                  maybe we should do to ensure that we have all the messages in
206                  case of a crash (which is one of the main reasons we have a
207                      log facility in the first place). */
208                 fprintf(outlog, "[%s] [%s] %s\n", date, lname, msg);
209                 fflush(outlog);
210         }
211 }
212
213 void linphone_gtk_log_hide(){
214         if (log_window)
215                 gtk_widget_hide(log_window);
216 }
217
218 void linphone_gtk_create_log_window(void){
219         GtkTextBuffer *b;
220         log_window=linphone_gtk_create_window("log");
221         b=gtk_text_view_get_buffer(GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview")));
222         gtk_text_buffer_create_tag(b,"red","foreground","red",NULL);
223         gtk_text_buffer_create_tag(b,"orange","foreground","orange",NULL);
224         /*prevent the log window from being destroyed*/
225         g_signal_connect (G_OBJECT (log_window), "delete-event",
226                 G_CALLBACK (gtk_widget_hide_on_delete), log_window);
227
228 }
229
230 void linphone_gtk_destroy_log_window(void){
231         GtkWidget *w=log_window;
232         g_static_mutex_lock(&log_mutex);
233         log_window=NULL;
234         gtk_widget_destroy(w);
235         g_static_mutex_unlock(&log_mutex);
236 }
237
238 void linphone_gtk_log_show(void){
239         gtk_widget_show(log_window);
240         gtk_window_present(GTK_WINDOW(log_window));
241 }
242
243 static void linphone_gtk_display_log(GtkTextView *v, OrtpLogLevel lev, const char *msg){
244         GtkTextIter iter,begin;
245         int off;
246         GtkTextBuffer *b;
247         const char *lname="undef";
248
249         b=gtk_text_view_get_buffer(v);
250         switch(lev){
251                 case ORTP_DEBUG:
252                         lname="debug";
253                         break;
254                 case ORTP_MESSAGE:
255                         lname="message";
256                         break;
257                 case ORTP_WARNING:
258                         lname="warning";
259                         break;
260                 case ORTP_ERROR:
261                         lname="error";
262                         break;
263                 case ORTP_FATAL:
264                         lname="fatal";
265                         break;
266                 default:
267                         g_error("Bad level !");
268         }
269
270         gtk_text_buffer_get_end_iter(b,&iter);
271         off=gtk_text_iter_get_offset(&iter);
272         gtk_text_buffer_insert(b,&iter,lname,-1);
273         gtk_text_buffer_get_end_iter(b,&iter);
274         gtk_text_buffer_insert(b,&iter,": ",-1);
275         gtk_text_buffer_get_end_iter(b,&iter);
276         gtk_text_buffer_insert(b,&iter,msg,-1);
277         gtk_text_buffer_get_end_iter(b,&iter);
278         gtk_text_buffer_insert(b,&iter,"\n",-1);
279         gtk_text_buffer_get_end_iter(b,&iter);
280         gtk_text_buffer_get_iter_at_offset(b,&begin,off);
281         if (lev==ORTP_ERROR || lev==ORTP_FATAL) gtk_text_buffer_apply_tag_by_name(b,"red",&begin,&iter);
282         else if (lev==ORTP_WARNING) gtk_text_buffer_apply_tag_by_name(b,"orange",&begin,&iter);
283         
284         while(gtk_text_buffer_get_char_count(b)>LOG_MAX_CHARS){
285                 GtkTextIter iter_line_after;
286                 gtk_text_buffer_get_start_iter(b,&iter);
287                 iter_line_after=iter;
288                 if (gtk_text_iter_forward_line(&iter_line_after)){
289                         gtk_text_buffer_delete(b,&iter,&iter_line_after);
290                 }
291         }
292         
293 }
294
295 static void stick_to_end(GtkTextView *v){
296         GtkTextBuffer *b;
297         GtkTextIter iter;
298         b=gtk_text_view_get_buffer(v);
299         gtk_text_buffer_get_end_iter(b,&iter);
300         gtk_text_view_scroll_to_iter(v,&iter,0,FALSE,1.0,0);
301 }
302
303 void linphone_gtk_log_scroll_to_end(GtkToggleButton *button){
304         if (gtk_toggle_button_get_active(button)){
305                 GtkTextView *v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
306                 stick_to_end(v);
307         }
308 }
309
310 /*
311  * called from Gtk main loop.
312 **/
313 gboolean linphone_gtk_check_logs(){
314         GList *elem;
315         GtkTextView *v=NULL;
316         if (log_window) v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
317         g_static_mutex_lock(&log_mutex);
318         for(elem=log_queue;elem!=NULL;elem=elem->next){
319                 LinphoneGtkLog *lgl=(LinphoneGtkLog*)elem->data;
320                 if (v) linphone_gtk_display_log(v,lgl->lev,lgl->msg);
321                 g_free(lgl->msg);
322                 g_free(lgl);
323         }
324         if (log_queue) g_list_free(log_queue);
325         log_queue=NULL;
326         g_static_mutex_unlock(&log_mutex);
327         if (v)
328                 linphone_gtk_log_scroll_to_end(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(log_window,"scroll_to_end")));
329         return TRUE;
330 }
331
332
333 /*
334  * Called from any linphone thread.
335  */
336 void linphone_gtk_log_push(OrtpLogLevel lev, const char *fmt, va_list args){
337         gchar *msg=g_strdup_vprintf(fmt,args);
338         LinphoneGtkLog *lgl=g_new(LinphoneGtkLog,1);
339         lgl->lev=lev;
340         lgl->msg=msg;
341         g_static_mutex_lock(&log_mutex);
342         log_queue=g_list_append(log_queue,lgl);
343         linphone_gtk_log_file(lev, msg);
344         g_static_mutex_unlock(&log_mutex);
345 }
346
347 void linphone_gtk_log_clear(void){
348         if (log_window){
349                 GtkTextIter end,begin;
350                 GtkTextView *v;
351                 GtkTextBuffer *b;
352                 v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
353                 b=gtk_text_view_get_buffer(v);
354                 gtk_text_buffer_get_start_iter(b,&begin);
355                 gtk_text_buffer_get_end_iter(b,&end);
356                 gtk_text_buffer_delete(b,&begin,&end);
357         }
358 }
359
360