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