]> sjero.net Git - linphone/blob - gtk/logging.c
Merge branch 'master' of git.savannah.nongnu.org:/srv/git/linphone
[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         }
210 }
211
212 void linphone_gtk_log_hide(){
213         if (log_window)
214                 gtk_widget_hide(log_window);
215 }
216
217 void linphone_gtk_create_log_window(void){
218         GtkTextBuffer *b;
219         log_window=linphone_gtk_create_window("log");
220         b=gtk_text_view_get_buffer(GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview")));
221         gtk_text_buffer_create_tag(b,"red","foreground","red",NULL);
222         gtk_text_buffer_create_tag(b,"orange","foreground","orange",NULL);
223         /*prevent the log window from being destroyed*/
224         g_signal_connect (G_OBJECT (log_window), "delete-event",
225                 G_CALLBACK (gtk_widget_hide_on_delete), log_window);
226
227 }
228
229 void linphone_gtk_destroy_log_window(void){
230         GtkWidget *w=log_window;
231         g_static_mutex_lock(&log_mutex);
232         log_window=NULL;
233         gtk_widget_destroy(w);
234         g_static_mutex_unlock(&log_mutex);
235 }
236
237 void linphone_gtk_log_show(void){
238         gtk_widget_show(log_window);
239         gtk_window_present(GTK_WINDOW(log_window));
240 }
241
242 static void linphone_gtk_display_log(OrtpLogLevel lev, const char *msg){
243         GtkTextIter iter,begin;
244         int off;
245         static GtkTextView *v=NULL;
246         GtkTextBuffer *b;
247         const char *lname="undef";
248
249         if (log_window==NULL) {
250                 return;
251         }
252
253         if (v==NULL) v=GTK_TEXT_VIEW(linphone_gtk_get_widget(log_window,"textview"));
254         b=gtk_text_view_get_buffer(v);
255         switch(lev){
256                 case ORTP_DEBUG:
257                         lname="debug";
258                         break;
259                 case ORTP_MESSAGE:
260                         lname="message";
261                         break;
262                 case ORTP_WARNING:
263                         lname="warning";
264                         break;
265                 case ORTP_ERROR:
266                         lname="error";
267                         break;
268                 case ORTP_FATAL:
269                         lname="fatal";
270                         break;
271                 default:
272                         g_error("Bad level !");
273         }
274
275         gtk_text_buffer_get_end_iter(b,&iter);
276         off=gtk_text_iter_get_offset(&iter);
277         gtk_text_buffer_insert(b,&iter,lname,-1);
278         gtk_text_buffer_get_end_iter(b,&iter);
279         gtk_text_buffer_insert(b,&iter,": ",-1);
280         gtk_text_buffer_get_end_iter(b,&iter);
281         gtk_text_buffer_insert(b,&iter,msg,-1);
282         gtk_text_buffer_get_end_iter(b,&iter);
283         gtk_text_buffer_insert(b,&iter,"\n",-1);
284         gtk_text_buffer_get_end_iter(b,&iter);
285         gtk_text_buffer_get_iter_at_offset(b,&begin,off);
286         if (lev==ORTP_ERROR || lev==ORTP_FATAL) gtk_text_buffer_apply_tag_by_name(b,"red",&begin,&iter);
287         else if (lev==ORTP_WARNING) gtk_text_buffer_apply_tag_by_name(b,"orange",&begin,&iter);
288         /*
289         gtk_text_buffer_get_end_iter(b,&iter);
290         gtk_text_view_scroll_to_iter(v,&iter,0,FALSE,0,0);
291         */
292         while(gtk_text_buffer_get_char_count(b)>LOG_MAX_CHARS){
293                 GtkTextIter iter_line_after;
294                 gtk_text_buffer_get_start_iter(b,&iter);
295                 iter_line_after=iter;
296                 if (gtk_text_iter_forward_line(&iter_line_after)){
297                         gtk_text_buffer_delete(b,&iter,&iter_line_after);
298                 }
299         }
300 }
301
302 gboolean linphone_gtk_check_logs(){
303         GList *elem;
304         g_static_mutex_lock(&log_mutex);
305         for(elem=log_queue;elem!=NULL;elem=elem->next){
306                 LinphoneGtkLog *lgl=(LinphoneGtkLog*)elem->data;
307                 linphone_gtk_display_log(lgl->lev,lgl->msg);
308                 g_free(lgl->msg);
309                 g_free(lgl);
310         }
311         if (log_queue) g_list_free(log_queue);
312         log_queue=NULL;
313         g_static_mutex_unlock(&log_mutex);
314         return TRUE;
315 }
316
317 void linphone_gtk_log_push(OrtpLogLevel lev, const char *fmt, va_list args){
318         gchar *msg=g_strdup_vprintf(fmt,args);
319         LinphoneGtkLog *lgl=g_new(LinphoneGtkLog,1);
320         lgl->lev=lev;
321         lgl->msg=msg;
322         g_static_mutex_lock(&log_mutex);
323         log_queue=g_list_append(log_queue,lgl);
324         linphone_gtk_log_file(lev, msg);
325         g_static_mutex_unlock(&log_mutex);
326 }
327